<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>database by narae :p</title>
    <link>https://dbbymoon.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Sat, 11 Apr 2026 11:41:55 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>dbbymoon</managingEditor>
    <item>
      <title>JPA 연관 관계가 있는 엔티티 생성 패턴</title>
      <link>https://dbbymoon.tistory.com/19</link>
      <description>&lt;h1&gt;&lt;b&gt;Objective&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  JPA 연관 관계가 있는 엔티티를 생성하기 위해서는 연관 엔티티를 var nullable 타입으로 선언하여 생성해야 합니다. 하지만 데이터로 보았을 때 해당 엔티티는 var 타입도 nullable 타입도 아니길 원하는 경우 val non-nullable 타입으로 설정하고 JPA 엔티티를 생성하는 패턴에 대해 공유합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;Background&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OrderEntity.kt&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;707&quot; data-origin-height=&quot;366&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdCHmY/btr2O1PUKJu/s5ki6ToJSTGOd2w2FRwgnk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdCHmY/btr2O1PUKJu/s5ki6ToJSTGOd2w2FRwgnk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdCHmY/btr2O1PUKJu/s5ki6ToJSTGOd2w2FRwgnk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdCHmY%2Fbtr2O1PUKJu%2Fs5ki6ToJSTGOd2w2FRwgnk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;707&quot; height=&quot;366&quot; data-origin-width=&quot;707&quot; data-origin-height=&quot;366&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OrderProductEntity.kt&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-03-09 오전 4.23.31.png&quot; data-origin-width=&quot;623&quot; data-origin-height=&quot;395&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cy3jkN/btr2PzFOk4x/KsFNqtz5iKo2KChbVhvTb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cy3jkN/btr2PzFOk4x/KsFNqtz5iKo2KChbVhvTb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cy3jkN/btr2PzFOk4x/KsFNqtz5iKo2KChbVhvTb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcy3jkN%2Fbtr2PzFOk4x%2FKsFNqtz5iKo2KChbVhvTb1%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;623&quot; height=&quot;395&quot; data-filename=&quot;스크린샷 2023-03-09 오전 4.23.31.png&quot; data-origin-width=&quot;623&quot; data-origin-height=&quot;395&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OrderService.kt&lt;/p&gt;
&lt;pre id=&quot;code_1678304640148&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Service
class OrderService(
	private val orderRepository : OrderRepository
) {
	fun saveOrder(param : OrderCreateParam) {
		val order = OrderEntity(
				orderId = param.orderId
		)
		val orderProductList = param.orderProductList.map {
				OrderProductEntity(
						order = order
				)
		}
		order.orderProductList = orderProductList

		orderRepository.save(order)
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OrderEntity 를 먼저 생성하고, 먼저 생성한 Order와 함께 OrderProductEntity를 생성하여 orderProductList를 set 해야 한다.&lt;/li&gt;
&lt;li&gt;번거로운 것은 둘째치고, orderProductList의 타입이 var이라서 언제 어디서 바뀔 수 있다는 단점이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rArr; 그래서 저는 orderProductList를 val로 사용하고 싶었습니다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;알고 있는 데이터들만 가지고 생성자를 호출하고, init 에서 처리하자&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CreateOrderParam.kt&lt;/p&gt;
&lt;pre id=&quot;code_1678304669056&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;data class CreateOrderParam(
    val userId: Long,
    val orderProductList: List&amp;lt;CreateOrderProductParam&amp;gt;
)

data class CreateOrderProductParam(
    val productId:  Long
)&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;생성자 파라미터로 넘길 클래스입니다. 이 클래스만 가지고 Service 코드에서 OrderEntity를 생성할 것입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OrderService.kt&lt;/p&gt;
&lt;pre id=&quot;code_1678304697302&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Service
class OrderService(
	private val orderRepository : OrderRepository
) {
	fun saveOrder(param : CreateOrderParam) {
		val order = OrderEntity(param)
		orderRepository.save(order)
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서비스 코드는 이렇게 될 것입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OrderEntity.kt&lt;/p&gt;
&lt;pre id=&quot;code_1678304725679&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Entity
@Table(name = &quot;ORDER&quot;)
class OrderEntity(createOrderParam: CreateOrderParam) {
    @Id
    @Column(name = &quot;ORDER_ID&quot;)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val orderId: Long? = null

    @OneToMany(
        cascade = [CascadeType.ALL],
        fetch = FetchType.LAZY,
        mappedBy = &quot;order&quot;,
        targetEntity = OrderProductEntity::class
    )
    val orderProductList: List&amp;lt;OrderProductEntity&amp;gt;

    @Column(name = &quot;USER_ID&quot;)
    val userId: Long

    init {
        this.userId = createOrderParam.userId
        this.orderProductList = createOrderParam.orderProductList.map {
            OrderProductEntity(
                createOrderProductParam = it,
                order = this
            )
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OrderEntity 의 init 에서 프로퍼티를 초기화하고, OrderProductEntity를 생성할 때 order로는 &lt;span style=&quot;background-color: #000000; color: #eb5757;&quot; data-token-index=&quot;1&quot;&gt;this&lt;/span&gt; 키워드를 통해 OrderEntity를 넘깁니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OrderProductEntity.kt&lt;/p&gt;
&lt;pre id=&quot;code_1678304755902&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Entity
@Table(name = &quot;ORDER_PRODUCT&quot;)
class OrderProductEntity(
    createOrderProductParam: CreateOrderProductParam,
    order: OrderEntity,
) {
    @Id
    @Column(name = &quot;ORDER_PRODUCT_ID&quot;)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val orderProductId: Long? = null

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = &quot;ORDER_ID&quot;, referencedColumnName = &quot;ORDER_ID&quot;)
    val order: OrderEntity

    @Column(name = &quot;PRODUCT_ID&quot;)
    val productId: Long

    init {
        this.order = order
        this.productId = createOrderProductParam.productId
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OrderProductEntity 에서는 생성자에서 받은 OrderEntity로 order를 초기화해줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코틀린의 init&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;생성자를 통해 인스턴스가 만들어질 때 호출되는 함수입니다.&lt;/li&gt;
&lt;li&gt;인스턴스가 만들어질 때, init 내부의 코드를 통해 프로퍼티를 초기화할 수 있습니다.&lt;/li&gt;
&lt;li&gt;여기서 발생할 수 있는 문제가 있는데요&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;kotlin.png&quot; data-origin-width=&quot;615&quot; data-origin-height=&quot;115&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJVsJy/btr2FewiDzX/HkySQCiBaHfNqr6ltKVYPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJVsJy/btr2FewiDzX/HkySQCiBaHfNqr6ltKVYPk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJVsJy/btr2FewiDzX/HkySQCiBaHfNqr6ltKVYPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJVsJy%2Fbtr2FewiDzX%2FHkySQCiBaHfNqr6ltKVYPk%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;615&quot; height=&quot;115&quot; data-filename=&quot;kotlin.png&quot; data-origin-width=&quot;615&quot; data-origin-height=&quot;115&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;init 내부에서 초기화하는 프로퍼티를 final 로 설정하라는 문구가 뜹니다.
&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/50222139/kotlin-calling-non-final-function-in-constructor-works&quot; data-token-index=&quot;0&quot;&gt;&lt;span&gt;참고 링크&lt;/span&gt;&lt;/a&gt;&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;이 경우 Derived 에서는 NPE 가 발생할 수 있습니다.&lt;/li&gt;
&lt;li&gt;Base의 init 코드를 수행 시 size 프로퍼티를 사용하는데, Derived의 size는 items.size 로 초기화하고 있습니다. 하지만 이 때, items는 초기화가 되지 않은 시점이니 NPE가 발생합니다.&amp;rArr; 따라서, open class의 init 내부에서 접근하는 어떤 프로퍼티는 하위 클래스에 의해 조작되어 예외가 발생할 수 있는 상황들이 있어 final 프로퍼티로 사용하는 것이 좋습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1678304868665&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;open class Base {
    open val size: Int = 0
    init { 
				println(&quot;size = $size&quot;) 
		}
}

class Derived : Base() {
    val items = mutableListOf(1, 2, 3)
    override val size: Int get() = items.size
}&lt;/code&gt;&lt;/pre&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;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Hibernate는 클래스의 인스턴스를 생성할 때 reflection 기법을 사용하여 객체를 초기화 한다.&lt;/li&gt;
&lt;li&gt;필드와 컬럼을 매핑하는 @Column 어노테이션을 통해 매핑할 수 있기 때문에 기본 생성자에 꼭 컬럼에 대응하는 필드들이 있어야 하는 것이 아닙니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;결론&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;연관 관계가 있는 엔티티를 생성할 때 연관 엔티티를 꼭 var nullable type으로 설정하지 않아도 생성할 수 있는 패턴을 적용해보았다.&lt;/li&gt;
&lt;li&gt;호불호가 갈릴 수 있기 때문에 팀원들의 의견이 궁금합니다.&lt;/li&gt;
&lt;li&gt;혹시 발생할 수 있는 문제는 없을지도 이야기 해주시면 감사하겠습니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>개발 노트</category>
      <author>dbbymoon</author>
      <guid isPermaLink="true">https://dbbymoon.tistory.com/19</guid>
      <comments>https://dbbymoon.tistory.com/19#entry19comment</comments>
      <pubDate>Thu, 9 Mar 2023 04:48:24 +0900</pubDate>
    </item>
    <item>
      <title>JPA @OneToMany - targetEntity</title>
      <link>https://dbbymoon.tistory.com/18</link>
      <description>&lt;h1&gt;&lt;b&gt;Objective&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  JPA의 @OneToMany 연관 관계에서 연관 엔티티가 open class 인 경우 read-only 컬렉션을 사용하지 못하고 Mutable 컬렉션을 사용하도록 하는데, 원인과 read-only 컬렉션을 사용할 수 있는 해결 방안을 공유합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;Background&lt;/b&gt;&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;build.gradle.kts&lt;/h3&gt;
&lt;pre id=&quot;code_1678300585510&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;allOpen {
	annotation(&quot;javax.persistence.Entity&quot;)
	annotation(&quot;javax.persistence.MappedSuperclass&quot;)
	annotation(&quot;javax.persistence.Embeddable&quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;OrderEntity.kt&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;809&quot; data-origin-height=&quot;277&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blnZ9L/btr2FhNiLrf/2TOKBkAe1lm3JlnavlupUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blnZ9L/btr2FhNiLrf/2TOKBkAe1lm3JlnavlupUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blnZ9L/btr2FhNiLrf/2TOKBkAe1lm3JlnavlupUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblnZ9L%2Fbtr2FhNiLrf%2F2TOKBkAe1lm3JlnavlupUK%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;809&quot; height=&quot;277&quot; data-origin-width=&quot;809&quot; data-origin-height=&quot;277&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;OrderProductEntity.kt&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;927&quot; data-origin-height=&quot;294&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Y0ZAG/btr2D66iFlt/kIk3tssexlVCa3MgjqDs21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Y0ZAG/btr2D66iFlt/kIk3tssexlVCa3MgjqDs21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Y0ZAG/btr2D66iFlt/kIk3tssexlVCa3MgjqDs21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FY0ZAG%2Fbtr2D66iFlt%2FkIk3tssexlVCa3MgjqDs21%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;927&quot; height=&quot;294&quot; data-origin-width=&quot;927&quot; data-origin-height=&quot;294&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Error Message&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;705&quot; data-origin-height=&quot;74&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceKxsq/btr2FdjWWum/Blg653ucwjQvbCSHqKLKiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceKxsq/btr2FdjWWum/Blg653ucwjQvbCSHqKLKiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceKxsq/btr2FdjWWum/Blg653ucwjQvbCSHqKLKiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FceKxsq%2Fbtr2FdjWWum%2FBlg653ucwjQvbCSHqKLKiK%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;705&quot; height=&quot;74&quot; data-origin-width=&quot;705&quot; data-origin-height=&quot;74&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;One To Many 속성 값의 데이터는 &amp;lsquo;? extends OrderProductEntity&amp;rsquo; 가 될 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1678300643427&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Collection has neither generic type or OneToMany.targetEntity() defined: 
moonee.order.storage.db.core.entity.OrderEntity.orderProductList&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컬렉션은 제네릭 타입을 가질 수 없거나, OneToMany의 targetEntity를 정의해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;원인&lt;/b&gt;&lt;/h1&gt;
&lt;pre id=&quot;code_1678300736755&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface List&amp;lt;out E&amp;gt; : Collection&amp;lt;E&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;List는 공변성을 가진다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공변적이다 ?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하위 타입 관계가 유지된다.&lt;/li&gt;
&lt;li&gt;A가 B의 하위 타입일 때, List&amp;lt;A&amp;gt;가 List&amp;lt;B&amp;gt;의 하위타입이다.&lt;/li&gt;
&lt;li&gt;A가 B의 하위 타입일 때, List&amp;lt;A&amp;gt; 타입의 값이 들어갈 자리에 List&amp;lt;B&amp;gt; 타입의 값을 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;코틀린의 클래스는 기본적으로 final이나, allOpen 설정을 통해 @Entity 클래스는 상속 가능한 클래스이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;orderProductList의 타입을 List&amp;lt;OrderProductEntity&amp;gt; 로 하게 되면, List&amp;lt;OrderProductEntity&amp;gt; 타입이 들어올 수도, OrderProductEntity 를 상속한 List&amp;lt;? extends OrderProductEntity 타입이 들어올 수도 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Hibernate는 Collection 타입에 대해 Generic 타입을 허용하지 않는다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;런타임에서 타입이 동적으로 바뀔 수 있는 것을 지원하지 않는다.&lt;/li&gt;
&lt;li&gt;뭔가 관련된 것 같은 문서를 발견했는데 정확한 해석이 어렵습니다. &lt;a href=&quot;https://docs.jboss.org/hibernate/core/3.3/reference/en/html/inheritance.html#inheritance-tableperconcreate-polymorphism&quot;&gt;(참고)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;Solution&lt;/b&gt;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Solution 1. MutableList를 사용한다 ?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Intellij 에서는 이 경우, MutableList를 사용하라고 권고한다.&lt;/li&gt;
&lt;li&gt;왜 MutableList는 될까?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;왜냐하면&amp;hellip;. MutableList는 무공변이다. out 키워드가 없음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rArr; 하지만 orderProductList는 변경되지 않는 데이터이기 때문에 MutableList로 사용하고 싶지 않다. (탈락)&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Solution 2. Entity를 상속 불가능하게 한다 ?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OrderProductEntity가 open class 이기 때문에, List 를 사용하면 정확히 어떤 타입이 들어올지 알 수 없으니 발생하는 오류라고 판단해서 allOpen 설정을 지워봤다.&lt;/li&gt;
&lt;li&gt;&amp;hellip;. 그러니까 해결은 됐다.&lt;/li&gt;
&lt;li&gt;하지만, 몇 주 전 케브님의 &lt;a href=&quot;https://www.notion.so/e916a1cecd8c4da1a0fe144bdc590d35&quot;&gt;JPA 지연로딩 뽀개기&lt;/a&gt; 에서 들었듯이, @~ToOne 관계에서 지연로딩을 할 때 JPA는 해당 클래스를 상속하여 프록시를 만드는 방식으로 지연 로딩을 한다고 했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rArr; 그래서 allOpen을 지우는 건 좋은 해결책은 아니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Solution 3. val 을 사용한다.&lt;/h2&gt;
&lt;pre id=&quot;code_1678300826973&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@OneToMany(cascade = [CascadeType.ALL], fetch = FetchType.LAZY, mappedBy = &quot;order&quot;)
val orderProductList: List&amp;lt;OrderProductEntity&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;var 대신 val 를 사용하면 생성할 때 초기화되고 바뀌지 않기 때문에 List 를 사용할 수 있는 것 같았다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rArr; 하지만 OrderProductEntity 는 order 또는 orderId 를 가지고 있기 때문에, OrderEntity를 생성하면서 동시에 OrderProductEntity를 생성할 수 없기 때문에 val 를 사용하기 어렵다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Solution 4. targetEntity 속성을 이용한다.&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;654&quot; data-origin-height=&quot;131&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/u9Bel/btr2UpbjNS0/ha7g05Dp0IaeifU1ZzN1u0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/u9Bel/btr2UpbjNS0/ha7g05Dp0IaeifU1ZzN1u0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/u9Bel/btr2UpbjNS0/ha7g05Dp0IaeifU1ZzN1u0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fu9Bel%2Fbtr2UpbjNS0%2Fha7g05Dp0IaeifU1ZzN1u0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;654&quot; height=&quot;131&quot; data-origin-width=&quot;654&quot; data-origin-height=&quot;131&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;i&gt;번역 / (선택 사항) 연결 대상인 엔터티 클래스입니다. 컬렉션 속성이 Java 제네릭을 사용하여 정의된 경우에만 선택 사항입니다. 달리 지정해야 합니다. 제네릭을 사용하여 정의된 경우 컬렉션의 매개 변수화된 유형이 기본값입니다.&lt;/i&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;/li&gt;
&lt;li&gt;아마 JPA 에게 OrderProductEntity 또는 ? extends OrderProductEntity 타입들 중 어떤 타입을 사용할지 정확하게 알려주는 것이니 해결이 되었다고 이해했습니다.&lt;/li&gt;
&lt;/ul&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;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;targetEntity 속성을 이용하자&lt;/li&gt;
&lt;li&gt;Hibernate의 implicit Polymorphism 에 대해 조금 공부해보고 싶은데 어렵다. (같이 보실 분 모집합니다)
&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://docs.jboss.org/hibernate/core/3.3/reference/en/html/inheritance.html#inheritance-tableperconcreate-polymorphism&quot;&gt;https://docs.jboss.org/hibernate/core/3.3/reference/en/html/inheritance.html#inheritance-tableperconcreate-polymorphism&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>개발 노트</category>
      <author>dbbymoon</author>
      <guid isPermaLink="true">https://dbbymoon.tistory.com/18</guid>
      <comments>https://dbbymoon.tistory.com/18#entry18comment</comments>
      <pubDate>Thu, 9 Mar 2023 03:43:51 +0900</pubDate>
    </item>
    <item>
      <title>테스트 주도 개발 주기의 유지</title>
      <link>https://dbbymoon.tistory.com/17</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 주도 개발로 배우는 객체 지향 설계와 실천&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5장. 테스트 주도 개발 주기의 유지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5.1. 각 기능을 인수 테스트로 시작하라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실패하는 인수 테스트를 작성하는 것으로 신기능을 작업하는데 착수한다. 인수테스트는 우리가 작성하려는 기능을 아직 시스템에서 갖추지 못했다는 사실을 보여주고 그 기능이 완성되기까지 진행 상황을 반영한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인수 테스트 : 응용 도메인에서 나온 용어만 이용한다. (기반 기술. db 등 용어가 아닌)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;=&amp;gt; 자동으로 확인 가능한 형태로 요구 사항을 정확하게 표현하면 분명하게 드러나지 않는 가정을 밝히는데 도움이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;=&amp;gt; 실패하는 테스트 덕에 요구사항을 충족하는 데 필요한 만큼의 기능만 구현하는 데 집중할 수 있어 기능을 완성할 가능성이 높아진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;=&amp;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;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인수 테스트는 단위 테스트를 거친 객체를 대상으로 통합 테스트를 수행하고, 프로젝트를 진행시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5.2. 회귀를 포착하는 테스트와 진행 상황을 측정하는 테스트를 분리하라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인수테스트 통과 주기 : 중첩 피드백 고리를 구동하는 엔진에 해당&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단위테스트, 통합테스트 : 개발 팀을 보조하고 빠르게 실행되어야 한다. 항상 통과해야 한다.&lt;/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;5.3. 테스트를 가장 간단한 성공 케이스로 시작하라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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&gt;&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; &amp;ldquo;피드백을 받을 수 있는&amp;rdquo; 가장 간단한 것을 테스트해라&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가장 간단한 성공 케이스로 테스트를 시작&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5.4. 읽고 싶어 할 테스트를 작성하라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트를 시스템이나 객체에서 수행할 행위로 가능한 &amp;ldquo;명확&amp;rdquo;하게 표현&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트가 잘 읽히면 그 다음으로 지원하는 기반 구조를 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트가 어떻게 해야 할지 기술하는 명확한 오류 메시지를 보이면서 예상대로 실패하면 보조적인 역할을 하는 코드를 충분히 구현했다는 사실을 알게 된다. 그러면 해당 테스트를 통과시키는 코드를 작성하기 시작하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5.5. 테스트가 실패하는 것을 지켜보라&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TDD 주기의 일부로 진단 절차 개선하기&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;=&amp;gt; 실패 시, 진단 정보를 확인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;=&amp;gt; 오류 메시지가 코드와 관련된 문제로 이끌 때까지 테스트 코드를 조정하고 테스트를 재실행&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;=&amp;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;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;=&amp;gt; 유용한 진단 정보를 생성하는 고생을 감내하면 테스트, 코드에서 해야할 일이 무엇인지 분명하게 하는데 도움이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5.6. 입력에서 출력 순서로 개발하라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 이벤트를 받는 객체에서 중간 계층을 거쳐 중심 도메인 모델로 나아간 다음, 외부에서 확인 가능한 응답을 생성하는, 다른 경계에 위치한 객체에까지 =&amp;gt; 시스템을 처음부터 끝까지 만들어 나간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;*** 새 도메인 모델 객체에 단위 테스트를 수행한 후 애플리케이션의 나머지 부분에 해당 객체를 끼워 넣는 식으로 시작하는 것이 쉬워 보일 지라도 나중에 통합 문제로 어려움을 겪을 수 있다. =&amp;gt; 불필요하거나 올바르지 않은 기능을 구현했을 수 있음. 도메인 모델을 먼저 작업할 때는 올바른 피드백을 받을 수 없기 떄문&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5.7. 메서드가 아닌 행위를 단위 테스트하라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 메서드 테스트를 먼저 생각함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;=&amp;gt; 메서드 테스트는 &amp;ldquo;목적&amp;rdquo;이 무엇인지 알려주지 않는다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;제공해야 하는 기능에 집중&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;*** 메서드 테스트로는 각 객체의 동작 방식, 즉 객체의 책임이 무엇이고, 객체의 여러 메서드가 함께 어떻게 동작하는지 이해하기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5.8. 테스트에 귀를 기울여라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트하기 어려운 코드 영역&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;=&amp;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;gt; 잠재적인 유지 보수 문제를 조기에 알려주는 경고 표시&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;*** 예상치 못한 변화를 예상하라 : 설계에 취약함이 보일 때 리팩토링을 통해 시스템 품질을 유지한다면 어떠한 변화가 일어나도 거기에 대응할 수 있을 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5.9. 주기의 미세 조정&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;너무 큰 단위로 테스트 수행 =&amp;gt; 코드의 모든 가능한 경로를 시도하는 &amp;ldquo;조합 폭발&amp;rdquo; 현상 발생&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;너무 작은 단위로 테스트 수행 =&amp;gt; (예를 들면, 클래스 수준으로만) 테스트는 쉽지만 함께 동작하지 않는 객체에 대한 문제는 놓치게 될 것&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TDD를 진행하며 취약한 부분을 찾아보고, 테스트 전략을 조정한다.&lt;/li&gt;
&lt;li&gt;로직에서 성가신 부분은 단위 테스트(아니면 단순화)가 더 필요할 수 있다.&lt;/li&gt;
&lt;li&gt;처리하지 않은 예외에는 통합 수준의 테스트가 더 필요할 수 있다.&lt;/li&gt;
&lt;li&gt;예상치&lt;span&gt; &lt;/span&gt;못한&lt;span&gt; &lt;/span&gt;시스템&lt;span&gt; &lt;/span&gt;실패에는&lt;span&gt; &lt;/span&gt;조사가&lt;span&gt; &lt;/span&gt;더&lt;span&gt; &lt;/span&gt;필요하거나&lt;span&gt;, &lt;/span&gt;테스트를&lt;span&gt; &lt;/span&gt;더&lt;span&gt; &lt;/span&gt;철저하게&lt;span&gt; &lt;/span&gt;해야할&lt;span&gt; &lt;/span&gt;수&lt;span&gt; &lt;/span&gt;있다&lt;span&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>테스트</category>
      <author>dbbymoon</author>
      <guid isPermaLink="true">https://dbbymoon.tistory.com/17</guid>
      <comments>https://dbbymoon.tistory.com/17#entry17comment</comments>
      <pubDate>Sun, 5 Dec 2021 17:54:16 +0900</pubDate>
    </item>
    <item>
      <title>테스트 주도 주기 시작</title>
      <link>https://dbbymoon.tistory.com/16</link>
      <description>&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;테스트는 인수테스트의 일환으로 전 구간을 대상으로 실행되어 시스템의 외부 인터페이스에 관해 필요한 피드백을 줘야하는데, 이는 자동화된 빌드/배포/테스트 주기 전체를 구현해야 함을 의미한다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4.1. 우선 동작하는 골격을 대상으로 테스트하라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lsquo;동작하는 골격&amp;rsquo;을 대상으로 빌드, 배포, 테스트하는 방법을 파악한 다음, 그 기반 구조를 이용해 유의미한 첫 기능에 대한 인수 테스트를 작성한다. 그 후에는 시스템의 나머지 부분을 대상으로 테스트 주도 개발을 진행할 수 있게 모든 것이 제자리에 놓일 것이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동작하는 골격 : 전 구간을 대상으로 자동 빌드, 배포, 테스트를 할 수 있는 실제 기능을 가장 얇게 구현한 조각을 말한다.&lt;/li&gt;
&lt;li&gt;테스트 과정에서의 배포 단계가 필요한 이유&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4.2. 동작하는 골격의 외형 결정&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동작하는 골격 : 애플리케이션의 개괄적인 구조를 결정하기 시작&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 설계하기 위해서는 &amp;lsquo;시스템의 목적&amp;rsquo;을 이해해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트의 요구 사항, 즉 기능적 요구사항과 비기능적 요구 사항을 고수준 관점에서 바라보고 의사 결정에 참고해야 한다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀에서 자신들의 해법(코드 작성 시작하기 전 반드시 취해야 할 필수적인 의사 결정)을 전체적으로 조망하는데 이바지하게끔 첫 테스트를 작성하는 과정을 활용해 프로젝트의 맥락을 짚어낸다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문제 이해&lt;/li&gt;
&lt;li&gt;대략적인 설계(아키텍처)&lt;/li&gt;
&lt;li&gt;자동화 : 빌드, 배포, 전 구간 테스트&lt;/li&gt;
&lt;li&gt;TDD&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;배포 가능 시스템&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4.3. 피드백 소스 구축&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;/li&gt;
&lt;li&gt;설계가 얼마나 잘 동작하는가&lt;/li&gt;
&lt;li&gt;코드 품질은 얼마나 높은가&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등&amp;hellip;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 도메인에 관해 얼마나 잘 이해하고 있는지, 현장에서 시스템을 볼 수 있다는 것이 고객의 우선 순위를 바꾸는 것이 아닌지에 대한 피드백을 받을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 이로서 철저한 회귀 테스트 모음을 갖게된다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;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;4.4. 불확실성은 일찍 드러내라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트를 나중에 했을 때 프로젝트에서 나타나는 불확실성&amp;nbsp;&lt;/p&gt;</description>
      <category>테스트</category>
      <author>dbbymoon</author>
      <guid isPermaLink="true">https://dbbymoon.tistory.com/16</guid>
      <comments>https://dbbymoon.tistory.com/16#entry16comment</comments>
      <pubDate>Sun, 5 Dec 2021 17:53:54 +0900</pubDate>
    </item>
    <item>
      <title>도구 소개</title>
      <link>https://dbbymoon.tistory.com/15</link>
      <description>&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.2. JUnit4&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리플렉션을 통해 클래스 구조를 파악한 후 해당 클래스 내에서 테스트를 나타내는 것을 모두 실행&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테스트 케이스 : @Test&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;Junit4에서는 테스트를 실행할 때 테스트 클래스의 새 인스턴스를 생성한 후 적절한 테스트 메서드를 호출한다. 매번 새 테스트 객체를 생성하기 때문에 각 테스트 간의 격리성을 확보할 수 있다.&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&gt;&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;테스트 대상 객체를 호출하고 그 결과를 단정(aseertion)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;- assertTrue()&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;- aseertNull()&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;- aseertEquals()&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예외 예상&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;@Test(expected=&amp;hellip;.Exception.class)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테스트 픽스처&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;: 테스트가 시작할 때 존재하는 고정된 상태&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;- 테스트가 반복 가능함을 보장. 테스트가 실행될 때마다 해당 테스트는 동일한 상태로 시작하므로 동일한 결과를 낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;- 테스트가 실행되기 전에 준비해서(set up) 테스트 실행이 완료된 후에 정리(tear down)한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;- 픽스처는 해당 테스트를 정의한 클래스에서 관리하고 객체의 필드에 저장된다. 같은 클래스에 정의된 테스트는 모두 동일한 픽스처를 가지고 시작하며, 실행될 때 해당 픽스처를 변경해도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;- 픽스처는 필드 초기화자(initi9ializer)에서 준비한다. 또 테스트 클래스의 생성자나 인스턴스 초기화자 블록에서 픽스처를 준비할 수도 있다. @Before, @After&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테스트 러너&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;: 클래스를 대상으로 리플렉션을 수행해 테스트를 찾아 해당 테스트를 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.3. 햄크레스트 매처와 assertThat()&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&gt;&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;매처 : 특정 객체가 어떤 조건과 일치하는지 알려주며, 해당 조건이나 객체가 어떤 조건과 일치하지 않는 이유를 기술한다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;assertThat(a, b).= assertTrue(a.matches(b))&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;3.4. jMock2: 목 객체&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목 객체를 동적으로 생성하여 목을 생성하려는 타입의 구현체를 직접 작성하지 않아도 된다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;테스트 대상 객체가 그것과 상호 작용 중인 목 객체를 어떻게 호출하고 목 객체가 거기에 반응해 어떻게 동작해야 할지를 지정하는 API를 제공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모조 객체 : 테스트 대상 객체의 콘텍스트. 그것과 이웃하는 객체를 표현&lt;/li&gt;
&lt;li&gt;목 객체 : 테스트가 실행되는 과정에서 테스트 대상 객체의 실제 이웃을 대신&lt;/li&gt;
&lt;li&gt;예상 구문 : 테스트 과정에서 테스트 대상 객체가 그것의 이웃을 어떻게 호출해야 하는지 기술&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;cafe24 거래대사&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그룹관리자 / 관리자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상점관리자 merchantId&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그룹관리자 companyType&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;onboarding - &amp;hellip;&amp;hellip;..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>테스트</category>
      <author>dbbymoon</author>
      <guid isPermaLink="true">https://dbbymoon.tistory.com/15</guid>
      <comments>https://dbbymoon.tistory.com/15#entry15comment</comments>
      <pubDate>Sun, 5 Dec 2021 17:51:24 +0900</pubDate>
    </item>
    <item>
      <title>객체를 활용한 테스트 주도 개발</title>
      <link>https://dbbymoon.tistory.com/14</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 주도 개발로 배우는 객체 지향 설계와 실천&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2장. 객체를 활용한 테스트 주도 개발&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2.1. 객체망&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체지향설계 : 객체 간의 의사소통에 집중.&lt;/li&gt;
&lt;li&gt;협업하는 객체의 망&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2.2 값과 객체&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;값 : 변하지 않는 양이나 크기&lt;/li&gt;
&lt;li&gt;객체 : 시간이 지남에 따라 상태가 변할지도 모르지만 식별자가 있는 계산 절차&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2.3. 메시지를 따르라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 객체와 쉽게 관계를 맺을 수 있게 객체를 설계하면, 고수준의 선언적 접근법이 주는 혜택을 누릴 수 있다. 객체가 일반적인 의사소통 패턴을 따르고 객체 간의 의존성이 명시적임을 의미한다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;의사소통 패턴 : 객체들이 다른 객체와 상호작용하는 방법을 관장하는 각종 규칙으로 구성&lt;/li&gt;
&lt;li&gt;&lt;span&gt;객체&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;역할 : 관련된 책임의 집합&lt;/li&gt;
&lt;li&gt;책임 : 어떤 과업을 수행하거나 정보를 알아야할 의무&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;2.4. 묻지 말고 답하라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호출하는 객체가 무엇을 원하는지 기술하고, 호출된 객체가 그러한 바를 어떻게 실현할 지 결정하게 해야한다. (디미터의 법칙)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체는 그것이 내부적으로 보유하고 있거나 메시지를 통해 확보한 정보만 가지고 의사결정을 내려야 한다.&lt;span&gt;&amp;nbsp;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2.5. 그래도 가끔은 물어라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호출자에 질의메서드를 추가해 가장 적절한 객체에 행위가 자리 잡아 행위에 이해하기 쉬운 이름이 생기고 테스트하기가 쉬워진다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2.6. 협력 객체의 단위 테스트&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테스트에 존재하는 대상 객체의 이웃을 다른 대체물(목 객체, mock object)로 대체&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 발생하는 이벤트에 대해 대상 객체가 가짜 이웃과 어떻게 상호 작용할지 지정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;= 예상 구문(expectation)&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&gt;&amp;nbsp;&lt;/span&gt;&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;2.7. 목 객체를 활용한 TDD 지원&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&gt;과정&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;테스트 핵심 구조&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단위 테스트는 대상 객체와 해당 객체를 둘러싼 환경 간의 관계를 명확하게 드러낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그곳에 있는 모든 객체를 생성하고 대상 객체와 해당 객체의 협력자 사이의 상호 작용에 관한 단정을 만들어 낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>테스트</category>
      <author>dbbymoon</author>
      <guid isPermaLink="true">https://dbbymoon.tistory.com/14</guid>
      <comments>https://dbbymoon.tistory.com/14#entry14comment</comments>
      <pubDate>Sun, 5 Dec 2021 17:51:06 +0900</pubDate>
    </item>
    <item>
      <title>테스트 주도 개발의 핵심은 무엇인가?</title>
      <link>https://dbbymoon.tistory.com/13</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 주도 개발로 배우는 객체 지향 설계와 실천&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1장. 테스트 주도 개발의 핵심은 무엇인가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.1 학습 과정으로서의 소프트웨어 개발&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어 프로젝트 : 사람 + 응용 분야 + 기술 등&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미처 예상하지 못한 요소가 많음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자들은 구성 요소의 동작 방식을 배우면서 프로젝트를 마무리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: 불확실한 변화를 예측하려면 경험이 늘어남에 따라 불확실성을 해결하는데 도움이 될 프로세스가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.2. 피드백은 가장 기본적인 도구다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경험에 의거한 피드백을 이용해 시스템과 그 용도에 관해 배운 후, 그 배운바를 시스템에 적용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포 : 내린 가정을 검사할 기회 =&amp;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;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;/li&gt;
&lt;li&gt;반복적인 개발 : 계속해서 충분한 상태에 이를 때까지 피드백에 응답해 기능 구현을 다듬는다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.3. 변화를 돕는 실천법&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;회귀 오류를 잡아줄 꾸준한 테스트 : 기존 기능을 망가뜨리지 않고도 새 기능을 추가할 수 있다.&lt;/li&gt;
&lt;li&gt;테스트 자동화 : 구축과 배포, 시스템 버전 변경에 드는 비용을 줄이려면 테스트를 자동화해야 한다.&lt;/li&gt;
&lt;li&gt;코드 단순화 : 코드 설계를 개선하고 단순화하고, 중복을 제거하고, 코드가 명확하게 자신의 역할을 표현하게끔 코드를 사용할 때마다 꾸준히 코드를 리팩토링해야한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TDD =&amp;gt; 테스트를 설계활동으로. 테스트를 사용해 코드에서 하고 싶은 바에 관한 생각을 명확하게 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 내내 테스트를 작성한다면 변경에 대한 자신감을 주는 자동화된 회귀테스트를 구축할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.4. 테스트 주도 개발 간단 정리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TDD&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&gt;과정&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;작성할 때의 장점&lt;/li&gt;
&lt;li&gt;실행할 때 장점&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;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;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리팩토링 : 기존 코드의 작동 방식을 바꾸지 않은 채로 기존 코드의 내부 구조를 변경하는 것. 구현하는 기능의 표현을 개선해 코드를 좀 더 유지보수할 수 있게 만드는 것.&lt;/li&gt;
&lt;li&gt;리팩터링은 이해하기 쉽고 안전할 정도로 규모가 작아야 한다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.5. 좀 더 큰 그림&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;/li&gt;
&lt;li&gt;즉, 직접 관련된 코드만 작성한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;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;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;진척도를 측정하는 수단&lt;/li&gt;
&lt;li&gt;테스트 스위트가 증가하면 시스템을 변경할 때 회귀 실패에서 보호받을 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실패하는 단위테스트 작성 -&amp;gt; 테스트 통과 -&amp;gt; 리팩터링&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코드 품질 유지&lt;/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;1.6. 전 구간 테스트&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인수테스트에서는 시스템 내부 코드를 가능한 한 직접 호출하지 말고 시스템 전 구간을 시험해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: 외부에서 유입되는 시스템하고만 상호 작용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.7. 테스트의 수준&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인수 테스트 : 전체 시스템이 동작하는가?&lt;/li&gt;
&lt;li&gt;통합 테스트 : 변경할 수 없는 코드를 대상으로 코드가 동작하는가?&lt;/li&gt;
&lt;li&gt;단위 테스트 : 객체가 제대로 동작하는가? 객체를 이용하기가 편리한가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.8. 외부 품질과 내부 품질&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;외부 품질 : 시스템이 고객과 사용자의 요구를 얼마나 잘 충족하는가(기능, 신뢰성, 가용성, 응답성 등)&lt;/li&gt;
&lt;li&gt;내부 품질 : 시스템이 개발자와 관리자의 요구를 얼마나 잘 충족하는가(이해하기 쉬운가, 변경하기 쉬운가 등) =&amp;gt; 시스템의 동작 방식을 안전하고 예상 가능한 상태로 바꿀 수 있게 만드는 것이 중요&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전구간 테스트&lt;/li&gt;
&lt;li&gt;단위 테스트&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;결합도 : 한 변경이 다른 것의 변경을 강제한다면 요소들이 결합된 상태&lt;/li&gt;
&lt;li&gt;응집도&lt;span&gt; : &lt;/span&gt;해당&lt;span&gt; &lt;/span&gt;요소의&lt;span&gt; &lt;/span&gt;책임이&lt;span&gt; &lt;/span&gt;의미&lt;span&gt; &lt;/span&gt;있는&lt;span&gt; &lt;/span&gt;단위를&lt;span&gt; &lt;/span&gt;형성하는지&lt;span&gt; &lt;/span&gt;나타내는&lt;span&gt; &lt;/span&gt;척도&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>테스트</category>
      <author>dbbymoon</author>
      <guid isPermaLink="true">https://dbbymoon.tistory.com/13</guid>
      <comments>https://dbbymoon.tistory.com/13#entry13comment</comments>
      <pubDate>Sun, 5 Dec 2021 17:50:48 +0900</pubDate>
    </item>
    <item>
      <title>도커 + ELK 분산 환경 셋팅</title>
      <link>https://dbbymoon.tistory.com/11</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;# 단일 서버&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1590295684760&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git clone https://github.com/deviantony/docker-elk.git
cd docker-elk&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1590297289109&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;vi elasticsearch/config/elasticsearch.yml

xpack.security.enabled: false&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1590296064380&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker stack deploy -c docker-stack.yml elk&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1590296077872&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker ps
docker service ls
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷, 2020-05-24 14-08-05.png&quot; data-origin-width=&quot;1855&quot; data-origin-height=&quot;236&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4b8pq/btqEozWY0mQ/xAKHYXtRGKIJhQHSpdDjD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4b8pq/btqEozWY0mQ/xAKHYXtRGKIJhQHSpdDjD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4b8pq/btqEozWY0mQ/xAKHYXtRGKIJhQHSpdDjD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4b8pq%2FbtqEozWY0mQ%2FxAKHYXtRGKIJhQHSpdDjD1%2Fimg.png&quot; data-filename=&quot;스크린샷, 2020-05-24 14-08-05.png&quot; data-origin-width=&quot;1855&quot; data-origin-height=&quot;236&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;815&quot; data-origin-height=&quot;645&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dz0dU1/btqEmKFnAdk/paLrKopKap001mjfnGEHZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dz0dU1/btqEmKFnAdk/paLrKopKap001mjfnGEHZk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dz0dU1/btqEmKFnAdk/paLrKopKap001mjfnGEHZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdz0dU1%2FbtqEmKFnAdk%2FpaLrKopKap001mjfnGEHZk%2Fimg.png&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;815&quot; data-origin-height=&quot;645&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷, 2020-05-24 14-16-58.png&quot; data-origin-width=&quot;1085&quot; data-origin-height=&quot;470&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvvuvD/btqEm2TlkJz/HGtbkXOuJ4As5tjtuIX6f1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvvuvD/btqEm2TlkJz/HGtbkXOuJ4As5tjtuIX6f1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvvuvD/btqEm2TlkJz/HGtbkXOuJ4As5tjtuIX6f1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvvuvD%2FbtqEm2TlkJz%2FHGtbkXOuJ4As5tjtuIX6f1%2Fimg.png&quot; data-filename=&quot;스크린샷, 2020-05-24 14-16-58.png&quot; data-origin-width=&quot;1085&quot; data-origin-height=&quot;470&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;## 레플리카 모드&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1590297605279&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;vi docker-stack.yml

  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.6.2
    ports:
      - &quot;9200&quot;
      - &quot;9300&quot;
    configs:
      - source: elastic_config
        target: /usr/share/elasticsearch/config/elasticsearch.yml
    environment:
      cluster.name: &quot;docker-cluster&quot;
      node.name: elk_elasticsearch.{{.Task.Slot}}
      discovery.seed_hosts: tasks.elasticsearch
      cluster.initial_master_nodes: elk_elasticsearch.1,elk_elasticsearch.2,elk_elasticsearch.3
      node.master: &quot;true&quot;
      node.data: &quot;true&quot;
      ES_JAVA_OPTS: &quot;-Xmx256m -Xms256m&quot;
      ELASTIC_PASSWORD: changeme
    networks:
      - elk
    deploy:
      mode: replicated
      replicas: 3&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1590297788859&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;vi elasticsearch/config/elasticsearch.yml

network.host: [ _eth0_, _eth1_, _lo_ ]
network.publish_host: _eth0_&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1590298851274&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo sysctl -w vm.max_map_count=262144
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷, 2020-05-24 14-31-35.png&quot; data-origin-width=&quot;1844&quot; data-origin-height=&quot;252&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lfZl5/btqEnI1dLVH/EhFGX7tcnq2fbQdk0JRr0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lfZl5/btqEnI1dLVH/EhFGX7tcnq2fbQdk0JRr0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lfZl5/btqEnI1dLVH/EhFGX7tcnq2fbQdk0JRr0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlfZl5%2FbtqEnI1dLVH%2FEhFGX7tcnq2fbQdk0JRr0k%2Fimg.png&quot; data-filename=&quot;스크린샷, 2020-05-24 14-31-35.png&quot; data-origin-width=&quot;1844&quot; data-origin-height=&quot;252&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;833&quot; data-origin-height=&quot;647&quot; data-filename=&quot;스크린샷, 2020-05-24 14-42-02.png&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KYmVj/btqEnIz8aBV/vjInhXeAWjobAIyMZN8OK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KYmVj/btqEnIz8aBV/vjInhXeAWjobAIyMZN8OK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KYmVj/btqEnIz8aBV/vjInhXeAWjobAIyMZN8OK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKYmVj%2FbtqEnIz8aBV%2FvjInhXeAWjobAIyMZN8OK0%2Fimg.png&quot; data-origin-width=&quot;833&quot; data-origin-height=&quot;647&quot; data-filename=&quot;스크린샷, 2020-05-24 14-42-02.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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷, 2020-05-24 14-43-12.png&quot; data-origin-width=&quot;1071&quot; data-origin-height=&quot;683&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TuxhK/btqEnIGS8Ab/PSJD0cMwWjHPXZNyHziXP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TuxhK/btqEnIGS8Ab/PSJD0cMwWjHPXZNyHziXP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TuxhK/btqEnIGS8Ab/PSJD0cMwWjHPXZNyHziXP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTuxhK%2FbtqEnIGS8Ab%2FPSJD0cMwWjHPXZNyHziXP0%2Fimg.png&quot; data-filename=&quot;스크린샷, 2020-05-24 14-43-12.png&quot; data-origin-width=&quot;1071&quot; data-origin-height=&quot;683&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&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;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;## 글로벌 모드&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1590299206587&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;vi docker-stack.yml

  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.6.2
    hostname: elasticsearch-{{.Node.Hostname}}
    ports:
      - &quot;9200:9200&quot;
      - &quot;9300:9300&quot;
    configs:
      - source: elastic_config
        target: /usr/share/elasticsearch/config/elasticsearch.yml
    environment:
      cluster.name: &quot;docker-cluster&quot;
      node.name: elasticsearch-{{.Node.Hostname}}
      discovery.seed_hosts: elasticsearch
      cluster.initial_master_nodes: elasticsearch-laptop
      node.master: &quot;true&quot;
      node.data: &quot;true&quot;
      ES_JAVA_OPTS: &quot;-Xmx256m -Xms256m&quot;
      ELASTIC_PASSWORD: changeme
    networks:
      - elk
    deploy:
      mode: global&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷, 2020-05-24 16-23-23.png&quot; data-origin-width=&quot;1699&quot; data-origin-height=&quot;236&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PZhxa/btqEnIUtaXy/tW6ljQK2gXpa6h0a4DYrs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PZhxa/btqEnIUtaXy/tW6ljQK2gXpa6h0a4DYrs1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PZhxa/btqEnIUtaXy/tW6ljQK2gXpa6h0a4DYrs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPZhxa%2FbtqEnIUtaXy%2FtW6ljQK2gXpa6h0a4DYrs1%2Fimg.png&quot; data-filename=&quot;스크린샷, 2020-05-24 16-23-23.png&quot; data-origin-width=&quot;1699&quot; data-origin-height=&quot;236&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷, 2020-05-24 16-24-39.png&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;646&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/by2lgW/btqEoUUc0T7/wtxCGXydaLdk2hQfhzQL70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/by2lgW/btqEoUUc0T7/wtxCGXydaLdk2hQfhzQL70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/by2lgW/btqEoUUc0T7/wtxCGXydaLdk2hQfhzQL70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fby2lgW%2FbtqEoUUc0T7%2FwtxCGXydaLdk2hQfhzQL70%2Fimg.png&quot; data-filename=&quot;스크린샷, 2020-05-24 16-24-39.png&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;646&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷, 2020-05-24 16-25-03.png&quot; data-origin-width=&quot;1093&quot; data-origin-height=&quot;483&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kQdSx/btqEoBm3tkL/Rx1gXQ1MzZobXYiWZ9m38k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kQdSx/btqEoBm3tkL/Rx1gXQ1MzZobXYiWZ9m38k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kQdSx/btqEoBm3tkL/Rx1gXQ1MzZobXYiWZ9m38k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkQdSx%2FbtqEoBm3tkL%2FRx1gXQ1MzZobXYiWZ9m38k%2Fimg.png&quot; data-filename=&quot;스크린샷, 2020-05-24 16-25-03.png&quot; data-origin-width=&quot;1093&quot; data-origin-height=&quot;483&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>개발 노트</category>
      <author>dbbymoon</author>
      <guid isPermaLink="true">https://dbbymoon.tistory.com/11</guid>
      <comments>https://dbbymoon.tistory.com/11#entry11comment</comments>
      <pubDate>Sun, 24 May 2020 14:51:52 +0900</pubDate>
    </item>
    <item>
      <title>도커 Docker 정리 1</title>
      <link>https://dbbymoon.tistory.com/10</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;- 컨테이너형 가상화 기술을 구현하기위한 상주 애플리케이션&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;-&amp;nbsp;이&amp;nbsp;애플리케이션을&amp;nbsp;조작하기&amp;nbsp;위한&amp;nbsp;명령행&amp;nbsp;도구(CLI)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;로&amp;nbsp;구성되는&amp;nbsp;프로덕트.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;애플리케이션&amp;nbsp;배포에&amp;nbsp;특화되어&amp;nbsp;있기&amp;nbsp;때문에&amp;nbsp;애플리케이션&amp;nbsp;개발&amp;nbsp;및&amp;nbsp;운영을&amp;nbsp;컨테이너&amp;nbsp;중심으로&amp;nbsp;할&amp;nbsp;수&amp;nbsp;있다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;-&amp;nbsp;컨테이너&amp;nbsp;:&amp;nbsp;도커가&amp;nbsp;만들어내는&amp;nbsp;게스트&amp;nbsp;운영체제&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;# 특징&amp;nbsp;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;## 개발환경 구축&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬&amp;nbsp;환경에&amp;nbsp;도커만&amp;nbsp;설치하면&amp;nbsp;몇줄짜리&amp;nbsp;구성파일(Dockerfile)과&amp;nbsp;명령어&amp;nbsp;한&amp;nbsp;줄로&amp;nbsp;애플리케이션이나&amp;nbsp;미들웨어가&amp;nbsp;이미&amp;nbsp;갖춰진&amp;nbsp;테스트용&amp;nbsp;가상환경(도커&amp;nbsp;컨테이너)을&amp;nbsp;빠르게&amp;nbsp;구축할&amp;nbsp;수&amp;nbsp;있다.&lt;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;size18&quot;&gt;&lt;b&gt;## 운영 환경&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커는&amp;nbsp;개발&amp;nbsp;후&amp;nbsp;운영&amp;nbsp;환경에&amp;nbsp;대한&amp;nbsp;배포나&amp;nbsp;애플리케이션&amp;nbsp;플랫폼으로&amp;nbsp;가능하다.&amp;nbsp;&lt;br /&gt;기존&amp;nbsp;가상화&amp;nbsp;소프트웨어보다&amp;nbsp;더&amp;nbsp;가볍게&amp;nbsp;동작하기&amp;nbsp;때문에,&amp;nbsp;테스트&amp;nbsp;환경&amp;nbsp;뿐만&amp;nbsp;아니라&amp;nbsp;운영&amp;nbsp;환경에서도&amp;nbsp;컨테이너를&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;##&amp;nbsp;이식성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬&amp;nbsp;머신의&amp;nbsp;도커&amp;nbsp;환경에서&amp;nbsp;실행하던&amp;nbsp;컨테이너를&amp;nbsp;다른&amp;nbsp;서버에&amp;nbsp;있는&amp;nbsp;도커&amp;nbsp;환경에&amp;nbsp;배포하거나,&amp;nbsp;반대로&amp;nbsp;다른&amp;nbsp;서버의&amp;nbsp;도커&amp;nbsp;환경에서&amp;nbsp;동작하던&amp;nbsp;컨테이너를&amp;nbsp;로컬로&amp;nbsp;가져올&amp;nbsp;수&amp;nbsp;있다.&lt;br /&gt;즉,&amp;nbsp;개발&amp;nbsp;환경과&amp;nbsp;운영&amp;nbsp;환경을&amp;nbsp;거의&amp;nbsp;동등하게&amp;nbsp;재현할&amp;nbsp;수&amp;nbsp;있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;##&amp;nbsp;컨테이너&amp;nbsp;간의&amp;nbsp;연동&lt;/b&gt;&lt;br /&gt;&lt;b&gt;##&amp;nbsp;클라우드&amp;nbsp;플랫폼&amp;nbsp;지원&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;# 도커의 기본 개념&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;## 컨테이너 가상화 기술&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가상화&amp;nbsp;소프트웨어&amp;nbsp;없이도&amp;nbsp;운영체제의&amp;nbsp;리소스를&amp;nbsp;격리해&amp;nbsp;가상운영체제로&amp;nbsp;만들&amp;nbsp;수&amp;nbsp;있다.&lt;br /&gt;이&amp;nbsp;가상운영체제를&amp;nbsp;'컨테이너'라고&amp;nbsp;한다.&amp;nbsp;&lt;br /&gt;컨테이너를&amp;nbsp;만들면서&amp;nbsp;발생하는&amp;nbsp;오버헤드는&amp;nbsp;다른&amp;nbsp;가상화&amp;nbsp;소프트웨어보다&amp;nbsp;더&amp;nbsp;적다.&amp;nbsp;빠르게&amp;nbsp;시작&amp;nbsp;및&amp;nbsp;종료할&amp;nbsp;수&amp;nbsp;있고&amp;nbsp;이에&amp;nbsp;들어가는&amp;nbsp;리소스도&amp;nbsp;적은&amp;nbsp;편이다.&amp;nbsp;&lt;br /&gt;컨테이너를&amp;nbsp;쉽게&amp;nbsp;만들고,&amp;nbsp;사용하고&amp;nbsp;버릴&amp;nbsp;수&amp;nbsp;있다.&lt;br /&gt;&amp;lt;-&amp;gt;&amp;nbsp;호스트&amp;nbsp;운영체제형&amp;nbsp;가상화&amp;nbsp;:&amp;nbsp;운영체제&amp;nbsp;위에서&amp;nbsp;가상화&amp;nbsp;소프트웨어를&amp;nbsp;사용해&amp;nbsp;하드웨어를&amp;nbsp;에뮬레이션&amp;nbsp;하는&amp;nbsp;방법으로&amp;nbsp;게스트&amp;nbsp;운영체제를&amp;nbsp;만드는&amp;nbsp;방식.&amp;nbsp;컨테이너&amp;nbsp;가상화와&amp;nbsp;비교했을&amp;nbsp;때&amp;nbsp;구조적으로&amp;nbsp;오버헤드가&amp;nbsp;더&amp;nbsp;크다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;### LXC (LinuX Containers)&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단일&amp;nbsp;컨트롤&amp;nbsp;호스트&amp;nbsp;상에서&amp;nbsp;여러&amp;nbsp;개의&amp;nbsp;고립된&amp;nbsp;리눅스&amp;nbsp;시스템(컨테이너)들을&amp;nbsp;실행하기&amp;nbsp;위한&amp;nbsp;운영&amp;nbsp;시스템&amp;nbsp;레벨&amp;nbsp;가상화&amp;nbsp;방법&lt;br /&gt;=&amp;gt;&amp;nbsp;시스템&amp;nbsp;컨테이너.&amp;nbsp;단순한&amp;nbsp;리소스&amp;nbsp;분리&amp;nbsp;목적으로&amp;nbsp;애플리케이션&amp;nbsp;배포&amp;nbsp;용도는&amp;nbsp;아님&lt;br /&gt;=&amp;gt;&amp;nbsp;LXC에서&amp;nbsp;복제한&amp;nbsp;애플리케이션을&amp;nbsp;다른&amp;nbsp;LXC&amp;nbsp;호스트에서&amp;nbsp;실행했을&amp;nbsp;때&amp;nbsp;LXC&amp;nbsp;설정&amp;nbsp;차이로&amp;nbsp;인해&amp;nbsp;기대했던&amp;nbsp;대로&amp;nbsp;애플리케이션이&amp;nbsp;동작하지&amp;nbsp;않는&amp;nbsp;등의&amp;nbsp;문제가&amp;nbsp;있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;### 도커 vs LXC&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;nbsp;호스트&amp;nbsp;운영&amp;nbsp;체제의&amp;nbsp;영향을&amp;nbsp;받지&amp;nbsp;않는&amp;nbsp;실행&amp;nbsp;환경&amp;nbsp;(Docker&amp;nbsp;Engine을&amp;nbsp;이용한&amp;nbsp;실행&amp;nbsp;환경&amp;nbsp;표준화)&lt;br /&gt;-&amp;nbsp;DSL(Dockerfile)을&amp;nbsp;이용한&amp;nbsp;컨테이너&amp;nbsp;구성&amp;nbsp;및&amp;nbsp;애플리케이션&amp;nbsp;배포&amp;nbsp;정의&lt;br /&gt;-&amp;nbsp;이미지&amp;nbsp;버전&amp;nbsp;관리&lt;br /&gt;-&amp;nbsp;레이어&amp;nbsp;구조를&amp;nbsp;갖는&amp;nbsp;이미지&amp;nbsp;포맷(차분&amp;nbsp;빌드가&amp;nbsp;가능)&lt;br /&gt;-&amp;nbsp;도커&amp;nbsp;레지스트리&amp;nbsp;(이미지&amp;nbsp;저장&amp;nbsp;서버&amp;nbsp;역할을&amp;nbsp;함)&lt;br /&gt;-&amp;nbsp;프로그램&amp;nbsp;가능한&amp;nbsp;다양한&amp;nbsp;기능의&amp;nbsp;API&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;##&amp;nbsp;Dockerfile&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너&amp;nbsp;정보를&amp;nbsp;Dockerfile&amp;nbsp;코드로&amp;nbsp;관리&lt;br /&gt;이&amp;nbsp;코드를&amp;nbsp;기반으로&amp;nbsp;복제&amp;nbsp;및&amp;nbsp;배포가&amp;nbsp;이루어지기&amp;nbsp;때문에&amp;nbsp;재현성이&amp;nbsp;높다.&lt;br /&gt;도커&amp;nbsp;이전에는&amp;nbsp;애플리케이션을&amp;nbsp;호스트&amp;nbsp;운영체제&amp;nbsp;또는&amp;nbsp;게스트&amp;nbsp;운영체제에&amp;nbsp;배포하여,&amp;nbsp;애플리케이션이&amp;nbsp;운영체제의&amp;nbsp;영향을&amp;nbsp;강하게&amp;nbsp;받는다.&amp;nbsp;&lt;br /&gt;도커는&amp;nbsp;컨테이너에&amp;nbsp;애플리케이션&amp;nbsp;실행환경이&amp;nbsp;함께&amp;nbsp;배포되는&amp;nbsp;방식.&amp;nbsp;아예&amp;nbsp;실행환경&amp;nbsp;자체를&amp;nbsp;배포하는&amp;nbsp;방식.&amp;nbsp;&lt;br /&gt;=&amp;gt;&amp;nbsp;따라서&amp;nbsp;도커는&amp;nbsp;환경의&amp;nbsp;영향을&amp;nbsp;덜&amp;nbsp;받고&amp;nbsp;배포가&amp;nbsp;간편.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;#&amp;nbsp;도커&amp;nbsp;스타일&amp;nbsp;체험하기&lt;/h2&gt;
&lt;p&gt;1. helloworld 쉘 스크립트 파일 생성&lt;/p&gt;
&lt;pre id=&quot;code_1589612456343&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/shecho&amp;nbsp;

&quot;Hello&amp;nbsp;World&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;2.&amp;nbsp;Dockerfile&amp;nbsp;생성&lt;/p&gt;
&lt;pre id=&quot;code_1589612469065&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FROM ubuntu:16.04

COPY helloworld /usr/local/bin
RUN chmod +x /usr/local/bin/helloworld

CMD [&quot;helloworld&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;FROM&amp;nbsp;절&amp;nbsp;:&amp;nbsp;컨테이너의&amp;nbsp;원형(틀)&amp;nbsp;역할을&amp;nbsp;할&amp;nbsp;도커&amp;nbsp;이미지(운영체제)를&amp;nbsp;정의&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;FROM&amp;nbsp;openjdk:8-jre&lt;br /&gt;-&amp;nbsp;RUN&amp;nbsp;절&amp;nbsp;:&amp;nbsp;도커&amp;nbsp;컨테이너&amp;nbsp;안에서&amp;nbsp;어떤&amp;nbsp;명령을&amp;nbsp;수행하기&amp;nbsp;위한&amp;nbsp;것&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RUN&amp;nbsp;adduser&amp;nbsp;-disabled-password&amp;nbsp;-gecos&amp;nbsp;''&amp;nbsp;grip&lt;br /&gt;-&amp;nbsp;COPY&amp;nbsp;절&amp;nbsp;:&amp;nbsp;파일을&amp;nbsp;도커&amp;nbsp;컨테이너&amp;nbsp;안의&amp;nbsp;경로에&amp;nbsp;복사&lt;br /&gt;-&amp;nbsp;CMD&amp;nbsp;절&amp;nbsp;:&amp;nbsp;완성된&amp;nbsp;이미지를&amp;nbsp;도커&amp;nbsp;컨테이너로&amp;nbsp;실행하기&amp;nbsp;전에&amp;nbsp;먼저&amp;nbsp;실행할&amp;nbsp;명령을&amp;nbsp;정의&lt;br /&gt;&lt;br /&gt;3.&amp;nbsp;도커에&amp;nbsp;이미지&amp;nbsp;빌드&lt;/p&gt;
&lt;pre id=&quot;code_1589612483806&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker image build -t helloworld:latest .&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;4.&amp;nbsp;도커&amp;nbsp;컨테이너&amp;nbsp;실행&lt;/p&gt;
&lt;pre id=&quot;code_1589612492995&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker container run helloworld:latest&lt;/code&gt;&lt;/pre&gt;</description>
      <category>개발 노트</category>
      <author>dbbymoon</author>
      <guid isPermaLink="true">https://dbbymoon.tistory.com/10</guid>
      <comments>https://dbbymoon.tistory.com/10#entry10comment</comments>
      <pubDate>Sat, 16 May 2020 16:01:45 +0900</pubDate>
    </item>
    <item>
      <title>더블 디스패치 double dispatch</title>
      <link>https://dbbymoon.tistory.com/9</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;의존 관계&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;Supplier의&amp;nbsp;변화가&amp;nbsp;Client에&amp;nbsp;영향을&amp;nbsp;주는&amp;nbsp;경우&amp;nbsp;&lt;br /&gt;-&amp;nbsp;Supplier가&amp;nbsp;Client의&amp;nbsp;필드&lt;br /&gt;-&amp;nbsp;Supplier가&amp;nbsp;Client&amp;nbsp;메소드의&amp;nbsp;파라미터&lt;br /&gt;-&amp;nbsp;Supplier가&amp;nbsp;Client의&amp;nbsp;로컬&amp;nbsp;변수&lt;br /&gt;-&amp;nbsp;Supplier로&amp;nbsp;메시지를&amp;nbsp;보냄&lt;br /&gt;&lt;br /&gt;=&amp;gt;&amp;nbsp;재사용&amp;nbsp;가능한&amp;nbsp;객체&amp;nbsp;지향&amp;nbsp;설계/개발이&amp;nbsp;어렵다.&amp;nbsp;&lt;br /&gt;Client는&amp;nbsp;재사용이&amp;nbsp;어렵다&lt;br /&gt;Client는&amp;nbsp;컴포넌트/서비스가&amp;nbsp;될&amp;nbsp;수&amp;nbsp;없다.&lt;br /&gt;&lt;br /&gt;오브젝트&amp;nbsp;패턴은&amp;nbsp;런타임시&amp;nbsp;바뀔&amp;nbsp;수&amp;nbsp;있는,&amp;nbsp;(상속&amp;nbsp;관계보다)&amp;nbsp;더&amp;nbsp;동적인&amp;nbsp;오브젝트&amp;nbsp;(의존)&amp;nbsp;관계를&amp;nbsp;다룬다.&lt;br /&gt;-&amp;nbsp;생성&amp;nbsp;관련&amp;nbsp;패턴&amp;nbsp;(Creational&amp;nbsp;Pattern)&amp;nbsp;:&amp;nbsp;객체&amp;nbsp;인스턴스&amp;nbsp;생성을&amp;nbsp;위한&amp;nbsp;패턴으로,&amp;nbsp;클라이언트와&amp;nbsp;그&amp;nbsp;클라이언트에서&amp;nbsp;생성해야&amp;nbsp;할&amp;nbsp;객체&amp;nbsp;인스턴스&amp;nbsp;사이의&amp;nbsp;연결을&amp;nbsp;끊어주는&amp;nbsp;패턴&lt;br /&gt;싱글턴,&amp;nbsp;팩토리&amp;nbsp;메소드,&amp;nbsp;추상&amp;nbsp;팩토리,&amp;nbsp;프로토타입,&amp;nbsp;빌더&amp;nbsp;패턴&lt;br /&gt;-&amp;nbsp;행동&amp;nbsp;관련&amp;nbsp;패턴&amp;nbsp;(Behavioral&amp;nbsp;Pattern)&amp;nbsp;:&amp;nbsp;클래스와&amp;nbsp;객체들이&amp;nbsp;상호작용하는&amp;nbsp;방법&amp;nbsp;및&amp;nbsp;역할을&amp;nbsp;분담하는&amp;nbsp;방법과&amp;nbsp;관련된&amp;nbsp;패턴&lt;br /&gt;스트래티지,&amp;nbsp;옵저버,&amp;nbsp;스테이트,&amp;nbsp;커맨드,&amp;nbsp;이터레이터,&amp;nbsp;템플릿&amp;nbsp;메소드,&amp;nbsp;인터프리터,&amp;nbsp;미디에이터,&amp;nbsp;역할&amp;nbsp;변경,&amp;nbsp;메멘토,&amp;nbsp;비지터&lt;br /&gt;-&amp;nbsp;구조&amp;nbsp;관련&amp;nbsp;패턴&amp;nbsp;(Structural&amp;nbsp;Pattern)&amp;nbsp;:&amp;nbsp;클래스&amp;nbsp;및&amp;nbsp;객체들을&amp;nbsp;구성을&amp;nbsp;통해서&amp;nbsp;더&amp;nbsp;큰&amp;nbsp;구조로&amp;nbsp;만들&amp;nbsp;수&amp;nbsp;있게&amp;nbsp;해&amp;nbsp;주는&amp;nbsp;것과&amp;nbsp;관련된&amp;nbsp;패턴&lt;br /&gt;데코레이터,&amp;nbsp;어댑터,&amp;nbsp;컴포지트,&amp;nbsp;퍼사드,&amp;nbsp;프록시,&amp;nbsp;브리지,&amp;nbsp;플라이웨이트&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Method dispatch&lt;/h3&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;어떤 메소드를 실행할지 결정하는 것&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;# static dispatch&lt;/h4&gt;
&lt;pre id=&quot;code_1578391101911&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Service {
    void run() {
        System.out.println(&quot;run()&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1578391108013&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    new Service().run();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;컴파일&amp;nbsp;되는&amp;nbsp;시점에&amp;nbsp;컴파일러가&amp;nbsp;어떤&amp;nbsp;클래스의&amp;nbsp;메소드를&amp;nbsp;수행하는지&amp;nbsp;알고&amp;nbsp;있고&amp;nbsp;바이트&amp;nbsp;코드도&amp;nbsp;남는다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;# dynamic dispatch&lt;/h4&gt;
&lt;pre id=&quot;code_1578391150114&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;abstract class Service {
    abstract void run();
}

class ServiceImpl extends Service {
    void run() {
        System.out.println(&quot;run()&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1578391158453&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    Service service = new ServiceImpl();
    service.run();
} &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;어떤 메소드를 실행하는지 컴파일 시점에 모름. 추상클래스의 메소드를 호출하는 것만 알고 있음.&lt;br /&gt;런타임&amp;nbsp;시점에&amp;nbsp;service에&amp;nbsp;할당된&amp;nbsp;객체가&amp;nbsp;무엇인지&amp;nbsp;확인하고&amp;nbsp;메소드를&amp;nbsp;실행함&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Double Dispatch&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;Dynamic Dispatch를 두 번 하는 것&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;* 예시. SNS, Post&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;1. &amp;nbsp;구현체에 따라 로직이 다르지 않은 경우&lt;/h4&gt;
&lt;pre id=&quot;code_1578391260243&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface Post {
    void postOn(SNS sns);
}
class Text implements Post {
    public void postOn(SNS sns) {
        // text -&amp;gt; sns
    }
}
class Picture implements Post {
    public void postOn(SNS sns) {
        // picture -&amp;gt; sns
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1578391266855&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface SNS {};
class Facebook implements SNS {

}
class Twitter implements SNS {

}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1578391340912&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    public static void main(String[] args) {
        List&amp;lt;Post&amp;gt; posts = Arrays.asList(new Text(), new Picture());
        List&amp;lt;SNS&amp;gt; sns = Arrays.asList(new Facebook(), new Twitter());

        posts.forEach(p -&amp;gt; sns.forEach(s -&amp;gt; p.postOn(s)));
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;SNS의 구현체에 따라 로직이 달라지는 경우를 고려하지 않음&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. SNS의&amp;nbsp;구현체에&amp;nbsp;따라&amp;nbsp;로직이&amp;nbsp;다른&amp;nbsp;경우&amp;nbsp;(분기문&amp;nbsp;사용)&lt;/h4&gt;
&lt;pre id=&quot;code_1578391388735&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface Post {
    void postOn(SNS sns);
}
class Text implements Post {
    public void postOn(SNS sns) {
        if(sns instanceof Facebook) {
            // text -&amp;gt; facebook
        } else if(sns instanceof Twitter) {
            // text -&amp;gt; twitter
        } else {
            throw new IllegalArgumentException();
        }
    }
}
class Picture implements Post {
    public void postOn(SNS sns) {
         if(sns instanceof Facebook) {
            // picture -&amp;gt; facebook
        } else if(sns instanceof Twitter) {
            // picture -&amp;gt; twitter
        } else {
            throw new IllegalArgumentException();
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1578391411044&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface SNS {};
class Facebook implements SNS {

}
class Twitter implements SNS {

}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;-&amp;nbsp;SNS의&amp;nbsp;새로운&amp;nbsp;구현체가&amp;nbsp;생기면&amp;nbsp;분기문을&amp;nbsp;추가해야&amp;nbsp;함&lt;br /&gt;-&amp;nbsp;만약&amp;nbsp;실수로&amp;nbsp;분기문을&amp;nbsp;추가하지&amp;nbsp;않으면&amp;nbsp;의도치&amp;nbsp;않게&amp;nbsp;exception&amp;nbsp;발생&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. SNS의&amp;nbsp;구현체에&amp;nbsp;따라&amp;nbsp;로직이&amp;nbsp;다른&amp;nbsp;경우&amp;nbsp;(메소드&amp;nbsp;오버로딩&amp;nbsp;사용.&amp;nbsp;static&amp;nbsp;dispatch)&lt;/h4&gt;
&lt;pre id=&quot;code_1578391446614&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface Post {
    void postOn(SNS sns);
}
class Text implements Post {
    public void postOn(Facebook facebook) {
        // text -&amp;gt; facebook
    }

    public void postOn(Twitter twitter) {
        // text -&amp;gt; twitter
    }
}
class Picture implements Post {
    public void postOn(Facebook facebook) {
        // picture -&amp;gt; facebook
    }

    public void postOn(Twitter twitter) {
        // picture -&amp;gt; twitter
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1578391455015&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface SNS {};
class Facebook implements SNS {

}
class Twitter implements SNS {

}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이전&amp;nbsp;코드와&amp;nbsp;달리&amp;nbsp;메소드&amp;nbsp;오버로딩을&amp;nbsp;사용해&amp;nbsp;분기문을&amp;nbsp;제거함&lt;br /&gt;&lt;br /&gt;하지만&amp;nbsp;다음&amp;nbsp;코드&amp;nbsp;중&amp;nbsp;&amp;nbsp;s&amp;nbsp;-&amp;gt;&amp;nbsp;p.postOn(s)&amp;nbsp;에서&amp;nbsp;컴파일&amp;nbsp;시점에&amp;nbsp;에러&amp;nbsp;발생&lt;/p&gt;
&lt;pre id=&quot;code_1578391470205&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    public static void main(String[] args) {
        List&amp;lt;Post&amp;gt; posts = Arrays.asList(new Text(), new Picture());
        List&amp;lt;SNS&amp;gt; sns = Arrays.asList(new Facebook(), new Twitter());

        posts.forEach(p -&amp;gt; sns.forEach(s -&amp;gt; p.postOn(s)));
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;-&amp;nbsp;메소드&amp;nbsp;오버로딩은&amp;nbsp;static&amp;nbsp;dispatch&amp;nbsp;이므로&amp;nbsp;컴파일&amp;nbsp;시점에&amp;nbsp;어떤&amp;nbsp;클래스의&amp;nbsp;메소드를&amp;nbsp;수행할지&amp;nbsp;알아야&amp;nbsp;함&lt;br /&gt;-&amp;nbsp;하지만&amp;nbsp;s는&amp;nbsp;SNS라는&amp;nbsp;interface의&amp;nbsp;타입이기&amp;nbsp;때문에&amp;nbsp;어떤&amp;nbsp;구현체(Facebook,&amp;nbsp;Twitter&amp;nbsp;등)의&amp;nbsp;타입인지&amp;nbsp;컴파일러가&amp;nbsp;알&amp;nbsp;수&amp;nbsp;없음&lt;br /&gt;&lt;br /&gt;오류&amp;nbsp;발생.&amp;nbsp;실패&amp;nbsp;!!!!!!!&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Double Dispatch 사용&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1578391510452&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface Post {
    void postOn(SNS sns);
}

class Text implements Post {
    public void postOn(SNS sns) {
        sns.post(this);
    }
}

class Picture implements Post {
    public void postOn(SNS sns) {
        sns.post(this);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1578391521291&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface SNS {
    void post(Text text);
    void post(Picture picture);
}

class Facebook implements SNS {
    public void post(Text text) {
        // text -&amp;gt; facebook
    }
    public void post(Picture picture) {
        // picture -&amp;gt; facebook
    }
}

class Twitter implements SNS {
    public void post(Text text) {
        // text -&amp;gt; twitter
    }
    public void post(Picture picture) {
        // picture -&amp;gt; twitter
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1578391529642&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    public static void main(String[] args) {
        List&amp;lt;Post&amp;gt; posts = Arrays.asList(new Text(), new Picture());
        List&amp;lt;SNS&amp;gt; sns = Arrays.asList(new Facebook(), new Twitter());

        posts.forEach(p -&amp;gt; sns.forEach(s -&amp;gt; p.postOn(s)));
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;-&amp;nbsp;한&amp;nbsp;단계를&amp;nbsp;더&amp;nbsp;거친&amp;nbsp;것&amp;nbsp;같지만,&amp;nbsp;분기문을&amp;nbsp;사용하지&amp;nbsp;않고&amp;nbsp;dynamic&amp;nbsp;dispatch를&amp;nbsp;두&amp;nbsp;번&amp;nbsp;사용&lt;br /&gt;-&amp;nbsp;Post&amp;nbsp;중&amp;nbsp;어떤&amp;nbsp;구현체의&amp;nbsp;postOn&amp;nbsp;메소드를&amp;nbsp;실행할지&amp;nbsp;dynamic&amp;nbsp;dispatch&amp;nbsp;한&amp;nbsp;번&amp;nbsp;사용&lt;br /&gt;-&amp;nbsp;postOn&amp;nbsp;메소드&amp;nbsp;내부에서&amp;nbsp;SNS&amp;nbsp;중&amp;nbsp;어떤&amp;nbsp;구현체의&amp;nbsp;post&amp;nbsp;메소드를&amp;nbsp;실행할지&amp;nbsp;dynamic&amp;nbsp;dispatch&amp;nbsp;한&amp;nbsp;번&amp;nbsp;사용&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;=&amp;gt;&amp;nbsp;새로운&amp;nbsp;구현체가&amp;nbsp;생기는&amp;nbsp;경우에는&amp;nbsp;다음과&amp;nbsp;같이&amp;nbsp;구현체에&amp;nbsp;대한&amp;nbsp;코드만&amp;nbsp;작성하면&amp;nbsp;됨&lt;/p&gt;
&lt;pre id=&quot;code_1578391556602&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Instagram implements SNS {
    public void post(Text text) {
        // text -&amp;gt; instagram
    }
    public void post(Picture picture) {
        // picture -&amp;gt; instagram
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;-&amp;nbsp;구현체를&amp;nbsp;새로&amp;nbsp;추가하는&amp;nbsp;것이&amp;nbsp;자유로움&amp;nbsp;=&amp;gt;&amp;nbsp;기존에&amp;nbsp;의존하던&amp;nbsp;코드에&amp;nbsp;직접적으로&amp;nbsp;영향을&amp;nbsp;주지&amp;nbsp;않는다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;=&amp;gt;&amp;nbsp;비지터&amp;nbsp;패턴으로&amp;nbsp;예를&amp;nbsp;들면,&amp;nbsp;SNS가&amp;nbsp;visitor&amp;nbsp;역할을&amp;nbsp;하고&amp;nbsp;Post의&amp;nbsp;메소드&amp;nbsp;postOn()이&amp;nbsp;accept()&amp;nbsp;역할&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Visitor Pattern&lt;/h3&gt;
&lt;p&gt;위의 코드로 예를 들면,&lt;br /&gt;&lt;br /&gt;-&amp;nbsp;Post는&amp;nbsp;SNS&amp;nbsp;타입의&amp;nbsp;어떤&amp;nbsp;구현체가&amp;nbsp;들어오는지&amp;nbsp;관심&amp;nbsp;없고,&amp;nbsp;postOn()&amp;nbsp;메소드&amp;nbsp;(accept)&amp;nbsp;를&amp;nbsp;제공&lt;br /&gt;-&amp;nbsp;SNS의&amp;nbsp;구현체(visistor)의&amp;nbsp;post()&amp;nbsp;메소드&amp;nbsp;(visit)&amp;nbsp;를&amp;nbsp;통해&amp;nbsp;실제&amp;nbsp;로직을&amp;nbsp;실행할&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;-&amp;nbsp;SNS의&amp;nbsp;구현체가&amp;nbsp;새로&amp;nbsp;추가되어도&amp;nbsp;Post에는&amp;nbsp;영향을&amp;nbsp;미치지&amp;nbsp;않음&lt;br /&gt;-&amp;nbsp;Post의&amp;nbsp;구현체가&amp;nbsp;추가된다면&amp;nbsp;?&amp;nbsp;어쩔&amp;nbsp;수&amp;nbsp;없이&amp;nbsp;SNS에&amp;nbsp;Post의&amp;nbsp;구현체를&amp;nbsp;처리하는&amp;nbsp;메소드를&amp;nbsp;다&amp;nbsp;추가해&amp;nbsp;줘야&amp;nbsp;함.&amp;nbsp;SNS의&amp;nbsp;구현체에&amp;nbsp;따라&amp;nbsp;로직이&amp;nbsp;다르지&amp;nbsp;않고&amp;nbsp;모두&amp;nbsp;공통&amp;nbsp;로직을&amp;nbsp;사용한다면&amp;nbsp;SNS를&amp;nbsp;abstract&amp;nbsp;class로&amp;nbsp;작성해서&amp;nbsp;사용해도&amp;nbsp;됨.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Visitor Proxy Pattern (hibernate)&lt;/h3&gt;
&lt;p&gt;* polimophic query&lt;/p&gt;
&lt;pre id=&quot;code_1578391618558&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;SNS&amp;gt; sns = repository.findSNS();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이&amp;nbsp;list의&amp;nbsp;요소들을&amp;nbsp;instanceof로&amp;nbsp;타입을&amp;nbsp;체크하면&amp;nbsp;실패한다.&amp;nbsp;&lt;br /&gt;-&amp;nbsp;JPA에서&amp;nbsp;각각의&amp;nbsp;요소들은&amp;nbsp;프록시&amp;nbsp;구조로&amp;nbsp;반환되기&amp;nbsp;때문에&amp;nbsp;타입은&amp;nbsp;SNS&amp;nbsp;타입이기&amp;nbsp;때문.&lt;br /&gt;-&amp;nbsp;따라서,&amp;nbsp;이&amp;nbsp;때는&amp;nbsp;반드시&amp;nbsp;visitor&amp;nbsp;패턴을&amp;nbsp;사용해야&amp;nbsp;함.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;=&amp;gt;&amp;nbsp;따라서,&amp;nbsp;프록시를&amp;nbsp;visitor로&amp;nbsp;해서&amp;nbsp;타입을&amp;nbsp;체크할&amp;nbsp;수&amp;nbsp;있다.&amp;nbsp;이를&amp;nbsp;일반화해서&amp;nbsp;proxy&amp;nbsp;visitor&amp;nbsp;pattern&amp;nbsp;이라고&amp;nbsp;함&lt;/p&gt;</description>
      <category>개발 노트</category>
      <category>double dispatch</category>
      <category>Dynamic dispatch</category>
      <category>java</category>
      <category>static dispatch</category>
      <category>Visitor pattern</category>
      <category>visitor proxy pattern</category>
      <category>토비의 봄</category>
      <author>dbbymoon</author>
      <guid isPermaLink="true">https://dbbymoon.tistory.com/9</guid>
      <comments>https://dbbymoon.tistory.com/9#entry9comment</comments>
      <pubDate>Tue, 7 Jan 2020 19:08:10 +0900</pubDate>
    </item>
  </channel>
</rss>