<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>LIMDINGDONG STUDIO</title>
    <link>https://limdingdong.tistory.com/</link>
    <description>림딩동의 IT &amp;amp; Design</description>
    <language>ko</language>
    <pubDate>Wed, 27 May 2026 14:19:15 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>림딩동</managingEditor>
    <image>
      <title>LIMDINGDONG STUDIO</title>
      <url>https://tistory1.daumcdn.net/tistory/2913728/attach/a0652a9b3bc24002a1c28483efafaba2</url>
      <link>https://limdingdong.tistory.com</link>
    </image>
    <item>
      <title>아이템 15. 클래스와 멤버의 접근 권한을 최소화하라</title>
      <link>https://limdingdong.tistory.com/37</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;가시성과 컴포넌트 설계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;잘 설계된 컴포넌트는 내부 구현정보를 외부 컴포넌트에 노출하지 않는다. 구현과 API를 깔끔히 분리하는 것이다. 구현정보의 노출은 가시성에 의해 결정된다. 이렇게 내부 구현정보를 프로그램 내부로 숨기는 기법을 &lt;b&gt;정보 은닉&lt;/b&gt; 이라고 한다. 정보 은닉은 OOP 의 대표적인 특성으로 개발, 테스트, 최적화, 적용, 분석, 수정에 용이한 프로그래밍을 가능하게 한다. 강점은 다음과 같이 정리할 수 있다.&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;li&gt;재사용성 : 외부 의존도가 낮은 응집도 높은 컴포넌트는 낯선 환경에서도 유용하게 쓰일 가능성이 크다&lt;/li&gt;
&lt;li&gt;제작 난이도 감소 : 개별 컴포넌트의 동작을 검증하기 용이하다&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;자바와 접근 제어 매커니즘&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;자바 언어에서 프로그램 요소(클래스, 메서드, 필드)의 접근성은 그 요소가 선언된 위치(패키지) 와 접근 제한자(private, protected, public, package) 로 정해진다. 가시성의 정의는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;public : 모든 곳에서 접근 가능. 인터페이스에 접근 제한자를 명시하지 않았을 때 자동 적용.&lt;/li&gt;
&lt;li&gt;protected : 멤버를 선언한 클래스의 하위 클래스에서 접근 가능. package-private 접근범위 포함.&lt;/li&gt;
&lt;li&gt;package-private (default) : 멤버가 소속된 패키지 안의 모든 클래스에서 접근 가능. 클래스에 접근 제한자를 명시하지 않았을 때 자동 적용.&lt;/li&gt;
&lt;li&gt;private : 멤버를 선언한 클래스에서만 접근 가능.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;가시성을 고려할 때는 다음 문장부터 시작해보자.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;모든 클래스와 멤버의 접근성을 가능한 한 좁혀야 한다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;클래스의 외부에서 접근할 수 있도록 허용하는 접근 지정자는 public, package-private 두 가지이다. public 지정자는 &lt;b&gt;공개 API&lt;/b&gt; 를 의미한다. 패키지 외부에서 사용할 일이 없는 요소에 대해서는 package-private 로 선언해보자. 이들은 API 가 아닌 내부 구현이 되어 수정 시의 영향범위가 자신이 속한 패키지 내부로 한정된다. public 선언으로 인한 공개 API는 하위 호환을 위해 영원히 관리해 주어야 한다.&lt;/p&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;클래스의 공개 API 를 설계한 후, 그 외 모든 멤버는 우선 private 로 선언하자. 그 이후에 타 클래스의 접근이 필요할 때마다 이에 맞추어 권한을 한 단계씩 낮추어 보자. 권한을 풀어주는 일이 반복된다면 컴포넌트의 분해 신호가 될 수도 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;접근 제어자 활용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;private, package-private 멤버는 보통 공개 API에 영향을 주지는 않는다. 단, Serializable 인터페이스를 구현한 클래스에서는 그 필드들도 의도치 않게 공개 API가 될 수 있다.&lt;/p&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;public 클래스의 protected 멤버는 공개 API로 봐야 한다. 이를 확장하는 클래스에서 접근할 수 있기 때문이다. 상위 클래스의 메서드를 재정의 할 때는 그 접근 수준을 좁힐 수 없다. (리스코프 치환 원칙) public 클래스의 인스턴스 필드는 public 으로 선언하지 말자. 필드와 관련된 불변식을 유지할 수 없으며, 스레드에 안전하지도 않다. 상수는 예외가 될 수 있다. 단, public static final 필드로 선언하여 불변하게 유지하자. 관례상 이런 상수의 이름은 언더바(_) 를 포함한 대문자로 명명한다.&lt;/p&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;테스트 목적으로 접근 범위를 넓히는 경우가 있다. 이렇게 해서는 안된다. 가시성이 낮은 요소들은 가시성이 높은 컴포넌트를 통해 호출될 수 있다. 공개 API 를 통해 테스트해보자. API가 너무 크다면 설계를 분해하자.&lt;/p&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;배열이나 컬렉션 같이 내부에 자료구조를 관리하는 클래스는 final 로 선언하더라도 요소에 대한 변경을 일으킬 수 있다. 배열에 대해서는 다음과 같이 불변 리스트를 활용하는 방법과 방어적 복사를 구현하는 방법으로 요소변경에 대한 문제를 방지할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;불변 리스트 활용 : public 배열을 private 로 선언하고, public 불변 리스트를 추가하는 방법&lt;/p&gt;
&lt;pre id=&quot;code_1724588813972&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private static final Thing[] PRIVATE_VALUES = {...};
public static final List&amp;lt;Thing&amp;gt; VALUES = Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES));&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;방어적 복사 : 배열을 private 로 선언하고, 복사본을 반환하는 public 메서드 추가&lt;/p&gt;
&lt;pre id=&quot;code_1724588895990&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private static final Thing[] PRIVATE_VALUES = {...};
public static final Thing[] values() {
	return PRIVATE_VALUES.clone();
}&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;자바 9의 모듈 시스템, 자바 17의 sealed class&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;객체지향 언어인 자바는 태생상 로직의 묶음을 표현하도록 설계되어 있다. 이 묶음을 어떠한 방식으로 노출할 것이냐는 고민에 따라 가시성의 레벨과 패키지가 생성된다. 시스템이 발전하고 규모가 커지면서, 이 묶음의 단위가 점점 많아진다. 클래스의 수가 점점 많아지면서 이를 정리하기 위한 계층의 깊이가 깊어지고, 패키지도 많아진다. 자바 9에서는 이 정리를 돕기 위해 모듈 시스템 이라는 개념이 도입되었다. 모듈은 패키지들의 묶음이다. 모듈은 자신이 속하는 패키지 중 공개할 것들을 선택해 선언한다. (관례 : module-info.java) 모듈 선언 내의 패키지 요소들은 public, protected 멤버라도 지정되지 않은 외부에서 접근할 수 없다.&lt;/p&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;자바 15에서는 확장(extends), 구현(implements) 범위에 제약을 가할 수 있는 sealed class 문법을 제공한다. super-calss 에 sealed 키워드를 사용하고 permits 키워드 뒤에 해당 클래스를 확장하거나 구현할 수 있는 클래스를 명시해 제한할 수 있다.&lt;/p&gt;</description>
      <category>프로그래밍/Effective Java</category>
      <category>effectivejava</category>
      <category>java</category>
      <category>이펙티브자바</category>
      <category>자바</category>
      <author>림딩동</author>
      <guid isPermaLink="true">https://limdingdong.tistory.com/37</guid>
      <comments>https://limdingdong.tistory.com/37#entry37comment</comments>
      <pubDate>Sun, 25 Aug 2024 21:36:57 +0900</pubDate>
    </item>
    <item>
      <title>아이템 14. Comparable 을 구현할 지 고려하라</title>
      <link>https://limdingdong.tistory.com/36</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;compareTo 메서드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Comparable 인터페이스는 compareTo 라는 제네렉 메서드를 단독으로 정의한다. compareTo 메서드는 단순 동시성 비교에 더해 순서까지 비교하는 스펙을 표현하고 있다. 이 인터페이스를 구현하는 것은 그 클래스의 인스턴스들에 자연적인 순서가 있음을 표현하는 행위가 된다. Comparable 인터페이스를 구현한 인스턴스들의 배열은 Arrays.sort(a) 유틸을 활용해 쉽게 정렬할 수 있다. 사실상 자바 라이브러리의 모든 값 클래스와 열거타입은 Comparable 을 구현하고 있다. 알파벳, 숫자, 연대 와 같이 &lt;b&gt;순서가 명확한 값 클래스&lt;/b&gt;를 작성한다면 Comparable 인터페이스를 구현해야 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;compareTo 메서드의 일반 규약&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 두 객체 참조의 순서를 바꿔 비교해도 예상한 결과가 나와야 한다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;sgn(x.compareTo(y)) == -sgn(y.compareTo(x))&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 첫 번째가 두 번째보다 크고, 두 번째가 세 번째 보다 크면, 첫 번째는 세 번째보다 커야한다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;(x.compareTo(y) &amp;gt; 0 &amp;amp;&amp;amp; y.compareTo(z) &amp;gt; 0) 이면 x.compareTo(z) &amp;gt; 0 이다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 크기가 같은 객체들끼리는 어떤 객체와 비교하더라도 항상 같아야 한다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 z에 대해 x.compareTo(y) == 0 이면 sgn(x.compareTo(z)) == sgn(y.compareTo(z)) 이다&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;(x.compareTo(y) == 0) == (x.equals(y)) 여야 한다.&lt;/li&gt;
&lt;li&gt;CompareTo 를 구현하고 이 권고를 지키지 않은 모든 클래스는 그 사실을 명시해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;일반 규약은 compareTo 메서드로 수행하는 동치성 검사에도 반사성, 대칭성, 추이성을 충족해야 함을 뜻한다. 이 때문에 기존 클래스를 확장한 구체 클래스에서 새로운 값 컴포넌트를 추가했다면 compareTo 규약을 지킬 방법이 없다. 확장하는 대신 독립된 클래스를 만들고, 이 클래스에 원래 클래스의 인스턴스를 가리키는 필드를 두는 우회법을 적용할 수 있다.&lt;/p&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;BigDecimal 클래스는 compareTo, equals 메서드의 일관성을 보장하지 않는다. 따라서 new 연산자를 통해 동일한 값을 갖는 두 BigDecimal 인스턴스를 생성하면 HashSet 과 TreeSet 에서 각각 다르게 동작한다. (HashSet 은 원소 두 개로 인식, TreeSet 은 원소 한 개로 인식)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;compareTo 메서드 작성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Comparable 은 제네릭 인터페이스이므로, 메서드 인수타입이 컴파일 시점에 정해진다. 인수타입이 잘못되는 경우 컴파일 자체가 이루어지지 않아 작성에 용이하다. null 을 인수로 넣어 호출할 경우 NullPointerException 을 던져야 함에 유의하자. 객체 참조 필드를 비교할 때에는 compareTo 메서드를 재귀적으로 호출하자. Comparable 인터페이스를 사용할 수 없는 상황에서는 Comparator 를 대신 사용한다.&lt;/p&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;Comparator 인터페이스는 수많은 보조 생성 메서드를 제공한다. 대표적으로 comparingInt, thenComparingInt 메서드가 있다. 이 메서드는 short 와 같은 더 작은 정수타입에 대해서도 함께 작동한다. double 버전 메서드를 사용하면 실수형 처리도 가능하다. 이와 같이 자바의 모든 숫자용 기본타입을 지원한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;정리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;값 객체를 활용하는 프로그램에서는 Comparable 인터페이스의 중요성이 더욱 높아지게 된다. 책에서는 compareTo 메서드에서 필드의 값을 비교할 때 &amp;lt; 와, &amp;gt; 연산자를 사용하지 말 것을 권장하고 있다. 개인적으로 필자는 비교연산자를 사용하되 비교 결과값을 0으로 고정하는 방식을 추천한다. 예를 들어 다음과 같은 문장을 해석해보겠다.&lt;/p&gt;
&lt;pre id=&quot;code_1723963651591&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (x.compareTo(y) &amp;gt; 0) ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;비교 결과값을 0으로 고정하면 0 앞의 연산자를 compareTo 문장의 자리로 가져오면 된다. 해석해보면 &lt;b&gt;if (x &amp;gt; y)&lt;/b&gt; 인 상황이 되는 것이다. compareTo 문장을 사용하려는 의도는 메서드의 결과값이 1, 0, -1 임을 외우는 것이 아니라 비교를 하는 것이다. 결과값에 초점을 두기보다 비교연산자에 초점을 두면 사용하기가 더 편리할 것이다.&lt;/p&gt;</description>
      <category>프로그래밍/Effective Java</category>
      <category>effectivejava</category>
      <category>java</category>
      <category>이펙티브자바</category>
      <category>자바</category>
      <author>림딩동</author>
      <guid isPermaLink="true">https://limdingdong.tistory.com/36</guid>
      <comments>https://limdingdong.tistory.com/36#entry36comment</comments>
      <pubDate>Sun, 18 Aug 2024 15:50:05 +0900</pubDate>
    </item>
    <item>
      <title>아이템 13. clone 재정의는 주의해서 진행하라</title>
      <link>https://limdingdong.tistory.com/35</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;Cloneable 인터페이스의 문제점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Cloneable 인터페이스는 클래스가 '복제가능한' 특성을 표현하기 위해 정의되었다. 이 인터페이스는 이렇게 정의되어 있다&lt;/p&gt;
&lt;pre id=&quot;code_1722753996827&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface Cloneable {
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;?????&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그렇다 문제는 Cloneable 인터페이스에 동작을 표현하는 메서드가 정의되어 있지 않다는 것이다. 그렇다면 Cloneable 인터페이스의 동작은 어디에 정의되어 있을까?&lt;/p&gt;
&lt;pre id=&quot;code_1722754357132&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Object {

    @IntrinsicCandidate
    protected native Object clone() throws CloneNotSupportedException;
    
...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;clone 메서드는 Object 클래스에 정의되어 있다. 심지어 protected 형태로 존재한다. 그래서 Cloneable 인터페이스를 구현한 구현체는 그 구현만으로는 clone 메서드를 호출할 수 없다. Cloneable 인터페이스는 Object 클래스에 정의된 clone 메서드의 동작방식을 결정하는 역할을 수행한다. Cloneable 인터페이스를 구현한 클래스의 인스턴스에서 clone 메서드를 호출하면 그 객체의 필드들을 하나씩 복사한 인스턴스를 반환하며, 그렇지 않은 클래스의 인스턴스에서 호출하면 CloneNotSupportedException 을 던진다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Cloneable 인터페이스의 모순&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;인터페이스를 구현한다는 것은 일반적으로 해당 클래스가 그 인터페이스에서 정의한 기능을 제공한다고 선언하는 행위를 의미한다. 그런데 Cloneable 인터페이스의 경우에는 상위클래스에 정의된 protected 메서드의 동작방식을 변경하는 행위로 정의되었다.&lt;/p&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;일반적인 인터페이스 규약을 생각한다면, 사용자는 Cloneable 인터페이스를 구현한 클래스가 clone 메서드를 public 가시성으로 제공하면서 인스턴스의 복제기능을 제공할 것이라고 기대할 것이다. 그런데 이 규약을 지키면 자바 언어 차원에서의 큰 모순이 하나 생긴다&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;생성자를 호출하지 않고도 객체를 생성할 수 있게 된다&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Cloneable 인터페이스를 구현할 때 유의해야 할 점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;제대로 동작하는 clone 메서드를 구현할 때에는 &lt;b&gt;해당 클래스가 가변 상태를 참조하는 지&lt;/b&gt;에 유의해야 한다. 반대로 생각하면 &lt;b&gt;불변 클래스는 굳이 clone 메서드를 제공하지 않는게 좋다&lt;/b&gt;는 의미이기도 하다. 가령 휴대폰을 소유한 사람 클래스가 존재한다고 가정해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1722755730603&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 Phone phone;

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 Person 클래스의 clone 메서드는 내부에 Phone 클래스 인스턴스의 복제로직을 반드시 호출해 주어야 한다. 그렇지 않으면 사람을 복제했을 때, 두 사람의 인스턴스가 동일한 휴대폰을 소유한 형태가 만들어질 수 있다. 이 때 phone 필드가 final 로 선언되어 있다면 다음과 같은 방식의 clone 재정의는 동작하지 않게 된다.&amp;nbsp;복제할 수 있는 클래스를 만들기 위해 final 한정자를 제거해야 할 수도 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1722756092160&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 final Phone phone;

    public Person clone() throws CloneNotSupportedException {
        Person person = (Person) super.clone();
        person.phone = phone.clone(); // final 선언으로 재할당 불가!
        return person;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;멤버 필드가 단일 인스턴스가 아닌 배열과 같은 형태의 자료구조로 구성되어 있다면, 더더욱 조심해야 한다. 자료구조를 복사했다고 생각했을 수 있으나, 자료구조를 구성하는 인스턴스들이 모두 복사되지 않았을 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Cloneable 인터페이스와 생성자&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Cloneable 인터페이스는 클래스의 새로운 인스턴스를 생성하는 기능을 제공하므로, 생성자 또는 팩토리 메서드와 비슷한 특성을 갖는다. 생성자를 작성할 때에는 주의해야 할 점이 있다. 바로 &lt;b&gt;생성자에서 재정의될 수 있는 메서드를 호출하지 말아야 한다&lt;/b&gt;는 것이다. (아이템 19) 간략하게 설명하자면 생성자의 로직은 인스턴스를 만드는 과정에 있기 때문에 하위 인스턴스의 동작이 기술될 수 있는 overridable 한 메서드를 호출해서는 안된다. 클래스의 인스턴스화가 완료되지 않은 상태에서 로직을 호출하는 것은 인스턴스가 불완전한 상태에서 상태변경을 시도하는 것이므로 매우 위험하다.&lt;/p&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;따라서 상속용 클래스는 Cloneable 인터페이스를 구현해서는 안된다. 명시적으로 ClassNotSupportedException throwing 로직을 작성해서 실수를 막는 것도 좋은 방법이다. 불가피하게 Cloneable 인터페이스를 구현한 클래스를 확장해야 한다면 어쩔 수 없이 clone 메서드가 잘 작동하도록 유의해서 구현해야 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;정리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;새로운 인터페이스를 만들때는 Cloneable 인터페이스를 확장해서는 안된다. 또한 클래스에서 Cloneable 인터페이스를 작성할 때에는 매우 신중해야 한다. 복제기능이 필요하다면 우선 생성자와 팩토리를 활용할 방법을 생각해보자. 배열에 대해서는 clone 메서드 방식이 깔끔하므로 메서드 사용이 권장된다.&lt;/p&gt;</description>
      <category>프로그래밍/Effective Java</category>
      <category>effectivejava</category>
      <category>java</category>
      <category>이펙티브자바</category>
      <category>자바</category>
      <author>림딩동</author>
      <guid isPermaLink="true">https://limdingdong.tistory.com/35</guid>
      <comments>https://limdingdong.tistory.com/35#entry35comment</comments>
      <pubDate>Sun, 4 Aug 2024 16:37:56 +0900</pubDate>
    </item>
    <item>
      <title>아이템 12. toString을 항상 재정의하라</title>
      <link>https://limdingdong.tistory.com/34</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;Object의 기본 toString 메서드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Object의 기본 toString 메서드는 PhoneNumber@adbbd 와 같은 형태로 &lt;b&gt;클래스_이름@16진수_해시코드&lt;/b&gt; 를 반환한다. toString 의 일반 규약은 &lt;b&gt;간결하면서 사람이 &lt;span style=&quot;color: #ee2323;&quot;&gt;읽기 쉬운 형태의 유익한&lt;/span&gt; 정보를 반환&lt;/b&gt;하도록 가이드하고 있다. toString의 규약은 모든 하위 클래스에서 이 메서드를 재정의하라 고 알려준다.&lt;/p&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;toString이 사용되는 대표적인 경우가 로깅이다. 이 메서드를 제대로 정의하지 않는다면 쓸모없는 메시지만 로그에 남게 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;좋은 toString 메서드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;toString을 구현할 때면 반환값의 포맷을 문서화할지 종해야 한다. 포맷을 명시하면 그 객체는 표준적이고, 명확하고, 사람이 읽을 수 있게 된다. 반면에 포맷을 명시함으로써 그 클래스가 포맷에 얽매이게 된다는 단점도 있다. 포맷을 맞추기 위한 작업을 모든 객체에 적용해 주어야 한다. 포맷이 바뀔 경우에는 변경공수가 커진다는 점도 있다.&lt;/p&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;포맷 명시 여부와 상관없이. toString 이 반환할 값에 포함된 정보를 얻어올 수 있는 API를 제공해야 한다. &quot;010-1234-5678&quot; 이라는 형태의 문자열을 반환해주는 전화번호 클래스의 toString 메서드를 구현했다고 가정하자. 이 때 전화번호 클래스는 통신사번호, 지역할당번호, 일련번호를 각각 반환할 수 있는 메서드를 제공해 주어야 한다. 그렇지 않으면 클라이언트는 클래스의 세부기능을 사용하기 위해 toString 메서드를 파싱하는 코드를 작성해야 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;정리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;toString 메서드도 실무에서 잘 사용하는 대상이라 익숙하다. 종종 toString 메서드를 프레임워크나 라이브러리와 결합해 활용하는 경우를 보게된다. 이 때에는 프레임워크나 라이브러리에서 인터페이스를 제공해주면 좋겠다는 생각이 든다. toString은 객체의 문자열 표현이다. 클래스를 정의한 작성자가 문자열 표현을 정의하는 것이기 때문에 프레임워크나 라이브러리의 관점에서 필요한 문자열이라면, 그 의도를 표현하는 인터페이스를 제공하는게 좋다고 생각한다.&lt;/p&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;롬복에서도 ToString 애노테이션을 제공한다. 이 때 해당 객체가 상속관계에 있다면 callSuper 옵션 설정을 고려해야 한다. 이 옵션의 기본값은 false이기 때문에, 상위클래스의 정보는 무시하게 된다.&lt;/p&gt;</description>
      <category>프로그래밍/Effective Java</category>
      <category>effectivejava</category>
      <category>java</category>
      <category>이펙티브자바</category>
      <category>자바</category>
      <author>림딩동</author>
      <guid isPermaLink="true">https://limdingdong.tistory.com/34</guid>
      <comments>https://limdingdong.tistory.com/34#entry34comment</comments>
      <pubDate>Sun, 14 Jul 2024 17:12:42 +0900</pubDate>
    </item>
    <item>
      <title>아이템 11. equals를 재정의하려거든 hashCode도 재정의하라</title>
      <link>https://limdingdong.tistory.com/33</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;hashCode 메서드를 재정의해야하는 이유&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;equals를 재정의한 클래스에서 hashcode 를 재정의하지 않으면, 일반 규약을 어기게 된다. 일반 규약을 어긴 클래스의 인스턴스는 HashMap 이나 HashSet 같은 hash 로직을 기반으로 하는 컬렉션의 원소로 사용할 때 문제를 일으키게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Object 명세 규약&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;equals 비교에 사용되는 정보가 변경되지 않았다면, 애플리케이션이 실행되는 동안 그 객체의 hashCode 메서드는 몇 번을 호출해도 일관되게 항상 같은 값을 반환해야 한다. 단, 애플리케이션 재실행시에는 이 값이 달라져도 상관없다.&lt;/li&gt;
&lt;li&gt;equals(Object) 가 두 객체를 같다고 판단했다면, 두 객체의 hashCode 는 같은 값을 반환해야 한다.&lt;/li&gt;
&lt;li&gt;equals(Object) 가 두 객체를 다르다고 판단했더라도, 두 객체의 hashCode가 서로 다른 값을 반환할 필요는 없다. 단, 다른 객체에 대해서는 다른 값을 반환해야 해시테이블의 성능이 좋아진다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 규약에서 두 번째 조항이 중요하다. 논리적으로 같은 객체에 대해서 같은 해시코드를 반환해 주어야 한다. 논리적으로 같은 객체를 물리적으로 다른 공간에 인스턴스화 하는경우 기본적인 hashCode 메서드는 서로 다른 값을 반환한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;좋은 해시 함수라면 서로 다른 인스턴스에 다른 해시코드를 반환한다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;좋은 해시코드를 작성하기 위한 요령&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;int 변수 result 를 선언한 후 값 c로 초기화한다. 이 때 c는&amp;nbsp; 해당 객체의 첫 번째 핵심필드를 단계 2.1 방식으로 계산한 해시코드이다.&lt;/li&gt;
&lt;li&gt;해당 객체의 나머지 핵심필드 f 각각에 대해 다음 작업을 수행한다&lt;br /&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;해당 필드의 해시코드 c를 계산한다
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;기본타입 필드라면 Type.hashCode(f)를 수행한다&lt;/li&gt;
&lt;li&gt;참조타입 필드면서 이 클래스의 equals 메서드가 이 필드의 equals 를 재귀적으로 호출한다면, 이 필드의 hashCode를 재귀적으로 호출한다. 필드의 값이 null 이면 0을 사용한다.&lt;/li&gt;
&lt;li&gt;필드가 배열이라면, 핵심 원소 각각을 별도 필드처럼 다룬다. 배열에 핵심 원소가 하나도 없다면 상수 0을 사용한다. 모든 원소가 핵심 원소라면 Arrays.hashCode를 사용한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;단계 2.1 에서 계산한 해시코드 c로 result를 갱신한다.&lt;br /&gt;ex) result = &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;31 * result&lt;/b&gt;&lt;/span&gt; + c;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;result를 반환한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;파생 필드는 해시코드 계산에서 제외해도 된다. 또한 equals 비교에 사용되지 않은 필드는 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;반드시&lt;/b&gt;&lt;/span&gt; 제외해야 한다. 그렇지 않으면 Object 명세규약의 두 번째 조항을 어기게 될 수 있다.&lt;/p&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;2-2 의 곱셈 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;31 * result&amp;nbsp;&lt;/b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;는 클래스에 비슷한 필드가 여러 개일 때 해시효과를 크게 높여준다. 곱하는 숫자가 31인 이유는 31이 홀수이면서 소수이기 때문이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;hashCode 메서드의 성능&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Object 클래스는 임의의 개수만큼 객체를 받아 해시코드를 계산해주는 hash static 메서드를 제공한다. 속도는 더 느리기 때문에 성능에 민감하지 않은 상황에서만 사용하는 것이 좋다.&lt;/p&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;클래스가 불변이고 해시코드를 계산하는 비용이 크다면, 매번 새로 계산하기 보다는 캐싱하는 방식을 고려해야 한다. 인스턴스 생성시에 해시코드를 계산해 둘 수 있다. hashCode 가 처음 불릴 때 계산하는 지연 초기화 전략을 택할 수도 있다. 대신에 그 클래스를 스레드에 안전하게 만들도록 설계해 주어야 한다.&lt;/p&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;h3 data-ke-size=&quot;size23&quot;&gt;hashCode 는 비즈니스 로직이 아니다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;hashCode가 반환하는 값의 생성 규칙을 API 사용자에게 자세히 공표할 필요는 없다. 클라이언트가 이 값에 의지해서는 안된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;정리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hashCode 메서드는 equals 메서드 구현에 따라오는 개념으로, 또 요즘에는 롬복과 같은 라이브러리가 제공하는 기능을 더 잘 이해하기 위한 개념으로만 인지하는 것 같다. Object 규약에 따라 equals 메서드와 hashCode 메서드의 의미에 미묘한 차이가 있는 것이 흥미롭다.&lt;/p&gt;</description>
      <category>프로그래밍/Effective Java</category>
      <category>effectivejava</category>
      <category>java</category>
      <category>이펙티브자바</category>
      <category>자바</category>
      <author>림딩동</author>
      <guid isPermaLink="true">https://limdingdong.tistory.com/33</guid>
      <comments>https://limdingdong.tistory.com/33#entry33comment</comments>
      <pubDate>Sun, 14 Jul 2024 16:59:44 +0900</pubDate>
    </item>
    <item>
      <title>아이템 10. equals는 일반 규약을 지켜 재정의하라</title>
      <link>https://limdingdong.tistory.com/32</link>
      <description>&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;equals 메서드의 재정의&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;어떤 객체가 논리적 동치성을 표현해야 하고, 상위 클래스의 equals 메서드가 논리적 동치성을 비교하지 않을 때&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Entity, ValueObject 와 같이 논리적 동치성을 보장해 주어야 하는 도메인 클래스 들에는 equals 메서드 재정의가 필요하다. equals 메서드를 재정의 할 때에는 반드시 Object 명세의 일반 규약을 따라야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;반사성 (reflexivity)&lt;br /&gt;: null이 아닌 모든 참조 값 x에 대해 x.equals(x)는 true다.&lt;/li&gt;
&lt;li&gt;대칭성 (symmetry)&lt;br /&gt;: null이 아닌 모든 참조 값 x, y에 대해, x.equals(y)가 true면 y.equals(x)도 true 다.&lt;/li&gt;
&lt;li&gt;추이성 (transitivity)&lt;br /&gt;: null이 아닌 모든 참조 값 x, y, z에 대해, x.equals(y)가 true이고, y.equals(z)도 true면 x.equals(z)도 true다.&lt;/li&gt;
&lt;li&gt;일관성 (consistency)&lt;br /&gt;: null이 아닌 모든 참조 값 x, y에 대해, x.equals(y)를 반복해서 호출하면 항상 true를 반환하거나 항상 false를 반환한다.&lt;/li&gt;
&lt;li&gt;null-아님&lt;br /&gt;: null이 아닌 모든 참조 값 x에 대해, x.equals(null)은 false다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;equals 메서드를 재정의할 필요가 없을 때&lt;/h3&gt;
&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: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;값을 표현하는게 아니라 동작하는 개체를 표현하는 클래스&lt;/li&gt;
&lt;li&gt;Thread&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;인스턴스의 논리적 동치성을 검사할 일이 없을 때&lt;/li&gt;
&lt;li&gt;상위 클래스에서 재정의한 equals 가 하위 클래스에서도 딱 들어맞을 때
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;AbstractSet, AbstractList, AbstractMap 등 컬렉션 패키지의 하위 구현체들&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;클래스 가시성이 private, package-private 이고, equals 를 호출할 일이 없을 때
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;equals 메서드의 호출실수를 방지하기 위해 예외를 던지는 코드를 구현하는 방법도 있다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1720337067213&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Override
public boolean equals(Object o) {
	throw new AssertionError();
}&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;인스턴스 통제 클래스 (싱글턴 인스턴스), Enum&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;객체지향 언어에서의 동치관계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;구체 클래스를 확장해 새로운 값을 추가하면서 equals 규약을 만족시킬 수 있는 방법은 존재하지 않는다. 리스코프 치환원칙은 &lt;b&gt;어떤 타입에 있어 중요한 속성은 그 하위타입에서도 마찬가지로 중요하다. 따라서 그 타입의 모든 메서드가 하위타입에서도 동일하게 잘 작동해야 한다&lt;/b&gt;고 명시하고 있다. 쉽게 설명하자면 &lt;b&gt;'Point 의 하위 클래스는 정의상 여전히 Point 이므로 어디서든 Point로써 활용될 수 있어야 한다'&lt;/b&gt; 로 풀어쓸 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;구체 클래스의 하위 클래스에서 값을 추가할 방법은 없지만 상속 대신 조합을 사용하는 방법으로 우회할 수는 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1720338165205&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public ColorPoint {
	private final Point point;
    private final Color color;
    
    @Override
    public boolean equals(Object o) {
    	if (!(o instanceof ColorPoint)) {
        	return false;
        }
        ColorPoint colorPoint = (ColorPoint)o;
        return colorPoint.point.equals(point) &amp;amp;&amp;amp; colorPoint.color.equals(color);
    }
    ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;자바 라이브러리에도 구체 클래스를 확장해 값을 추가한 클래스가 종종 있다. java.sql.Timestamp 는 java.util.Date 를 확장한 후 nanoseconds 필드를 추가했다. 그 결과로 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Timestamp&lt;span&gt; 클래스의 equals 는 대칭성을 위반하게 되었다. Timestamp 객체는 Date 객체와 한 컬렉션에 넣거나 서로 섞어 사용할 때 엉뚱하게 동작할 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;추상클래스의 하위 클래스에서는 equals 규약을 지키면서도 값을 추가할 수 있다. 아무런 값을 갖지 않는 추상클래스 Shape 을 상위에 정의하고 이를 확장하여 radius 필드를 추가한 Circle 클래스, length, width 필드를 추가한 Rectangle 클래스를 정의할 수 있다. 상위 클래스를 직접 인스턴스로 만드는게 불가능하다면 equals 규약과 관련한 문제들은 일어나지 않는다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;양질의 equals 메서드 구현방법&lt;/span&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;== 연산자를 사용해 입력이 자기자신의 참조인지 확인한다&lt;/li&gt;
&lt;li&gt;instanceof 연산자로 입력이 올바른 타입인지 확인한다&lt;/li&gt;
&lt;li&gt;입력을 올바른 타입으로 형변환한다. (인스턴스 검사를 통과했기 때문에 이 형변환은 100% 성공한다)&lt;/li&gt;
&lt;li&gt;입력 객체와 자기 자신의 대응되는 &lt;b&gt;핵심 필드들이 모두 일치&lt;/b&gt;하는지 하나씩 검사한다
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;float와 double을 제외한 기본타입 필드는 == 연산자로 비교한다&lt;/li&gt;
&lt;li&gt;참조타입 필드는 각각의 equals 메서드로 비교한다&lt;/li&gt;
&lt;li&gt;float, double 필드는 각각 정적 메서드 Float.compare, Double.compare 를 활용한다 (부동소수 처리)&lt;/li&gt;
&lt;li&gt;배열의 모든 원소가 핵심 필드라면 Arrays.equals 메서드를 활용한다&lt;/li&gt;
&lt;li&gt;null 을 정상 값으로 취급하도록 설계한 클래스라면 Object.equals 를 활용해 NPE를 방지한다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;비교하기가 아주 복잡한 필드를 가진 클래스의 경우 필드의 표준형을 저장해 둔 후 표준형끼리 비교할 수 있다. 특히 불변클래스에서 제격이다. 예컨대 휴대폰번호를 분할해 구현한 클래스의 경우 식별번호+국번+사번 을 문자열로 미리 캐싱해두고 이 값을 비교하게 구현할 수 있겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;데이터중심적 설계에서 객체지향 설계를 적용하게 되면 equals, hashcode 메서드 설계실수로 인해 난감한 상황을 자주 맞이하게 된다. 요즘은 IDE의 자동완성 기능이나 롬복과 같은 라이브러리를 통해 양질의 equals 메서드를 쉽게 작성할 수 있다. 보다 중요한건 자신이 설계한 객체의 아이덴티티(식별성)를 어떻게 부여하는가에 있다고 생각한다.&lt;/p&gt;</description>
      <category>프로그래밍/Effective Java</category>
      <category>effectivejava</category>
      <category>java</category>
      <category>이펙티브자바</category>
      <category>자바</category>
      <author>림딩동</author>
      <guid isPermaLink="true">https://limdingdong.tistory.com/32</guid>
      <comments>https://limdingdong.tistory.com/32#entry32comment</comments>
      <pubDate>Sun, 7 Jul 2024 16:56:30 +0900</pubDate>
    </item>
    <item>
      <title>아이템 9. try-finally 보다는 try-with-resources 를 사용하라</title>
      <link>https://limdingdong.tistory.com/31</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;자원을 정리하기 위한 try-finally 구문&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;자바 라이브러리에는 close 메서드를 호출해 직접 닫아주어야 하는 자원이 많다. (In/OutputStream, java.sql.Connection 등) 자원 닫기는 클라이언트가 놓치지 쉬워서 예측할 수 없는 성능문제로 이어지기도 한다. 전통적으로 자원을 닫기 위한 수단으로 try-finally 구문을 활용했다.&lt;/p&gt;
&lt;pre id=&quot;code_1720334679669&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;BufferedReader br = new BufferedReader(new FileREader(filePath));
try {
	return br.readLine();
} finally {
	br.close();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 코드에서 기기에 물리적 결함이 발생하는 경우, readLine 메서드와 close 메서드는 모두 실패하게 된다. 이 때 첫 번째 발생한 예외는 남지 않게되어 시스템에서의 디버깅이 어려워진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;AutoCloseable 구현과 try-with-resources 구문 사용&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;자바 7에서 try-with-resource 구문이 정의되었다. 이 구조를 사용하려면 사용대상 자원에 AutoCloseable 인터페이스를 구현해 주어야 한다. 이 구문은 다음과 같이 활용할 수 있다&lt;/p&gt;
&lt;pre id=&quot;code_1720335206378&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;try (InputStream in = new FileInputStream(source);
	OutputStream out = new FileOutputStream(destination)) {
    
    byte[] buffer = new byte[BUFFER_SIZE];
    int n;
    while ((n=in.read(buf)) &amp;gt;= 0)
    	out.write(buf, 0, n);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 구문에서는 try 예약어 다음에 소괄호를 기술하고, 자원 해제 대상을 미리 선언해 주어야 한다. 이후 중괄호 블록의 종료 후에 AutoCloseable 인터페이스의 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;&amp;nbsp;위 코드의 read 메서드에서 예외가 발생하는 경우, 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;&amp;nbsp;보통의 try-finally 처럼 try-with-resources 구문에서도 catch 절을 작성할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;정리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근에는 자바 7 미만의 버전을 사용하는 라이브 시스템은 거의 없는 것 같다. try-with-resources 구문은 이미 충분히 대중화되었다. 내가 설계할 클래스에 AutoCloseable 인터페이스의 구현 필요성과 자원 정리 개념을 어떻게 정의할 것인지 잘 고민해 보아야 겠다.&lt;/p&gt;</description>
      <category>프로그래밍/Effective Java</category>
      <category>effectivejava</category>
      <category>java</category>
      <category>이펙티브자바</category>
      <category>자바</category>
      <author>림딩동</author>
      <guid isPermaLink="true">https://limdingdong.tistory.com/31</guid>
      <comments>https://limdingdong.tistory.com/31#entry31comment</comments>
      <pubDate>Sun, 7 Jul 2024 15:59:32 +0900</pubDate>
    </item>
    <item>
      <title>아이템 8. finalize와 cleaner 사용을 피하라</title>
      <link>https://limdingdong.tistory.com/30</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;자바의 객체 소멸자&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;자바는 finalizer 와 cleaner 라는 두 가지 객체소멸자를 제공한다. finalizer는 예측할 수 없고, 상황에 따라 위험할 수 있어 일반적으로 불필요하다. cleaner는 finalizer 보다는 덜 위험하지만, 여전히 예측할 수 없고, 느리고, 일반적으로 불필요하다&lt;/p&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;자바의 객체 소멸자는 C++의 파괴자(destructor) 와는 다른 개념이다. C++의 파괴자가 자원회수를 명시적으로 표현하는 반면, finaizer와 cleaner는 즉시 수행된다는 보장이 없다. 이 소멸자로는 제때 실행되어야 하는 작업은 절대 할 수 없다. 상태를 영구적으로 수정하는 작업에서는 절대 자바의 객체 소멸자를 사용해서는 안된다. 데이터베이스 공유자원의 영구 락 해제를 finalizer나 cleaner 에 맡겨놓으면 시스템 전체가 서서히 멈출 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;객체 소멸자의 위험성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;finalizer와 cleaner는 심각한 성능 문제를 동반한다. 이 방식은 일반적인 자원 해제에 사용하는 AutoCloseable 인터페이스의 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;&amp;nbsp;finalizer는 심각한 보안 문제도 가지고 있다. 객체생성 또는 직렬화 과정에서 예외가 발생하면 생성되다 만 객체의 하위 클래스에서 finalizer 가 수행될 수도 있다. final 클래스들은 하위 클래스를 만들 수 없으니 이 공격에서 안전하지만, final 이 아닌 클래스의 경우 우 아무일도 수행하지 않는 finalize 메서드를 final 로 선언해서 공격을 막을 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;finalizer 와 cleaner 대체안&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;파일이나 스레드 등 종료해야 할 자원에 대해서는 자원의 정리를 명시해 주어야 한다. finalizer, cleaner 대신에 AutoCloseable 인터페이스의 close 메서드를 구현해주면 된다. 클라이언트에서는 인스턴스를 다 쓰고나서 close 메서드를 호출해 주어야 한다. close 메서드의 명시적 호출보다는 try-with-resource 구문을 사용하는게 더 좋다.&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;finalizer 와 cleaner&lt;span&gt; 는 어디에 쓰는 것일까?&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;nbsp;그럼 자바의 객체 소멸자는 대체 어디에 사용해야 할까? 첫 번째는 자원의 소유자가 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;AutoCloseable.&lt;/span&gt;close 메서드를 호출하지 않는 것에 대비한 안정망 역할로 사용할 수 있다. 자바 라이브러리의 FIleIn/OutputStream, ThreadPoolExecutor 클래스는 안전망 역할의 finalizer 소멸자를 제공한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;nbsp;두 번째 예는 네이티브 피어와 연결된 객체에서 쓰인다. 네이티브 피어란 일반 자바 객체가 네이티브 메서드를 통해 기능을 위임한 네이티브 객체를 말한다. (JNI - 하드웨어 또는 운영체제 종속 프로그램, C, C++, 어셈블리 언어로 작성된 라이브러리의 호출 인터페이스) 자바 피어를 회수할 때 네이티브 객체를 회수하기 위해 소멸자를 활용할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;정리&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;nbsp;이번 아이템에서는 조금 깊은 개념을 다뤘다. 전공수업때 배운 자바언어 실행원리를 복습하는 느낌이랄까... AutoCloseable 인터페이스의 중요성을 한 번 더 짚었다는 생각으로 정리된 것 같다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>프로그래밍/Effective Java</category>
      <category>effectivejava</category>
      <category>java</category>
      <category>Spring</category>
      <category>생성자</category>
      <category>소멸자</category>
      <category>이펙티브자바</category>
      <category>자바</category>
      <author>림딩동</author>
      <guid isPermaLink="true">https://limdingdong.tistory.com/30</guid>
      <comments>https://limdingdong.tistory.com/30#entry30comment</comments>
      <pubDate>Sun, 7 Jul 2024 15:38:00 +0900</pubDate>
    </item>
    <item>
      <title>아이템 7. 다 쓴 객체참조를 해제하라</title>
      <link>https://limdingdong.tistory.com/22</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;자바의 가비지 컬렉션&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;C, C++ 과 같이 메모리를 직접 관리해야 하는 언어와 달리, 자바는 가비지 컬렉션을 지원한다. 자바의 가비지 컬렉션은 기본적으로 참조를 기반으로 한다. 객체 인스턴스의 쓰임새를 판단해 &lt;b&gt;쓰임이 다 했다고 판단되는 자원을 정리&lt;/b&gt;하는 원리이다. 개발자는 메모리를 직접 관리하지 않아도 되지만 적어도 &lt;b&gt;객체의 쓰임이 끝났다는 것을 표현&lt;/b&gt;해 주어야 한다. 그렇지 않으면 개발자가 인지하지 못하는 메모리 누수가 발생하여 프로그램의 예기치 못한 종료를 야기할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;객체 참조를 해제하는 법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;객체 참조를 해제하는 가장 단순한 방법은 &lt;b&gt;참조변수에 null 을 할당&lt;/b&gt;하는 것이다. 피참조 지정을 잃은 인스턴스는 쓰임새가 다했다는 판단의 범위로 들어가게 된다. 하지만 이 단순한 방법은 예외적인 경우에 활용해야 한다. 모든 객체를 다 쓰자마자 일일히 null 처리하는 코드는 결국 메모리를 직접 관리하는 코드와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;다 쓴 참조를 해제하는 가장 좋은 방법은 그 참조를 담은 변수를 유효 범위(scope) 밖으로 밀어내는 것이다. &lt;b&gt;변수를 밀어낸다&lt;/b&gt; 라는 표현이 어색할 수 있다. 풀어서 작성해보면 &lt;b&gt;변수를 정의할 때 이 변수의 사용범위가 어디까지가 될 지 생각해서 정의해보자&lt;/b&gt; 가 되겠다. 일반적인 지역변수, 인스턴스 내부에서 사용되는 전역변수 들은 지역 스코프의 종료, 인스턴스가 사용된 스코프의 종료와 함께 가비지 컬렉팅의 대상이 된다,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;null 처리는 자기 메모리를 직접 관리하는 클래스에서 필요하다. Stack 과 같이 내부에서 배열의 인스턴스와 참조 포인터를 운영하는 클래스는 내부 메모리 관리가 필요하다. 아래와 같이 Stack 클래스의 pop 메서드는 상위 클래스인 Vector 의 removeElementAt 메서드를 통해 내부 메모리 해제를 명시적으로 수행한다.&lt;/p&gt;
&lt;pre id=&quot;code_1720332334757&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    public synchronized E pop() {
        E       obj;
        int     len = size();

        obj = peek();
        removeElementAt(len - 1);

        return obj;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1720332350447&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    public synchronized void removeElementAt(int index) {
        if (index &amp;gt;= elementCount) {
            throw new ArrayIndexOutOfBoundsException(index + &quot; &amp;gt;= &quot; +
                                                     elementCount);
        }
        else if (index &amp;lt; 0) {
            throw new ArrayIndexOutOfBoundsException(index);
        }
        int j = elementCount - index - 1;
        if (j &amp;gt; 0) {
            System.arraycopy(elementData, index + 1, elementData, index, j);
        }
        modCount++;
        elementCount--;
        elementData[elementCount] = null; /* to let gc do its work */
    }&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;메모리 누수원인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;캐시 또한 메모리 누수를 일으킨다. 객체 참조를 캐시에 넣고나서 해제해주지 않으면 누수가 발생할 수 있다. 캐시를 설계할 때는 캐싱 자원의 유효기간을 염두해야 한다. 유효기간을 정확히 정의하기 어렵다면 주기적인 캐시 청소방안을 설계해 주는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;실무에서 객체 참조 해제에 대해 깊숙히 고민해 본 적이 많지 않은 것 같다. 최근에는 더 견고한 프로그램을 설계하기 위해 임의의 자료구조나 캐시를 설계하는 경우가 생기고 있다. 메모리를 다루는 설계를 적용할 때에는 인스턴스 해제를 고려해야겠다는 생각이 든다.&lt;/p&gt;</description>
      <category>프로그래밍/Effective Java</category>
      <category>effectivejava</category>
      <category>java</category>
      <category>JVM</category>
      <category>Spring</category>
      <category>메모리</category>
      <category>이펙티브자바</category>
      <category>인스턴스</category>
      <category>자바</category>
      <author>림딩동</author>
      <guid isPermaLink="true">https://limdingdong.tistory.com/22</guid>
      <comments>https://limdingdong.tistory.com/22#entry22comment</comments>
      <pubDate>Sun, 7 Jul 2024 15:14:07 +0900</pubDate>
    </item>
    <item>
      <title>[포스트뱅킹] ⑤ 디지털 플랫폼 시장과 금융IT업계의 미래</title>
      <link>https://limdingdong.tistory.com/27</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;디지털 플랫폼 시장의 타 산업군&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;금융산업은 규제와 보수성이라는 특성상 타 산업군에 비해 변화의 속도가 느릴 수 밖에 없다. 금융산업이 타 산업과 동일한 방향으로 흘러가지는 않겠지만, 디지털 트랜스포메이션이라는 메가트렌드적 시선으로 접근해보면 타 산업군의 현재에서 미래 금융산업의 일부를 찾을 수도 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;앱&amp;middot;리테일 분석서비스 와이즈앱&amp;middot;리테일&amp;middot;굿즈는 2022년 1분기 이커머스 서비스의 결제액 순위를 발표했다. 1위부터 차례대로 쿠팡, 네이버, SSG닷컴, 배달의민족, 11번가 순이다. 이커머스 시장은 유통산업군에서 디지털 트랜스포메이션에 의해 창출된 신시장이라고 할 수 있다. 그런데 여기서 중요한 포인트를 찾아야 한다. 하나는 SSG 닷컴의 3위라는 순위는 이베이코리아의 인수로 인해 지마켓, 옥션, G9 등 서비스의 합산 점유율을 기준으로 한다는 점이다. 또 하나는 우리나라의 유통공룡인 '롯데' 의 이커머스 서비스인 롯데온을 순위에서 찾아볼 수 없다는 점이다. 심지어 SSG닷컴과 롯데온의 순수 점유율은 유통인프라 또는 디지털 플랫폼을 갖추지 않은 11번가보다도 낮다.&lt;/p&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;현재는 이커머스 시장이 성숙해지면서 롯데와 신세계도 자체적인 디지털 역량을 키우고, 과감한 인수합병을 진행한 상태다. 그러나 여전히 신세계는 업계의 선두로 올라서지 못했고, 롯데는 아직 이렇다 할 반전의 카드를 찾지 못한 모양새다. 왜 이커머스 시장에서 전통적인 대기업들은 오프라인에서 만큼의 영향력을 보여주지 못했을까? 우리는 여기서 쿠팡, 네이버, 이베이, 배달의민족, 11번가와 롯데, 신세계의 디지털 내재화 역량 차이에 주목해야 한다.&lt;/p&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;과거의 롯데와 신세계는 디지털 서비스를 그룹의 IT서비스 계열사에 외주형태로 위임하고 있었다. 앞서 언급한 금융IT 업계와 같이 이들은 비효율적인 체계를 기반으로 본질에서 벗어난 형태의 서비스를 제공하고 있었을 확률이 높다.&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;671&quot; data-origin-height=&quot;463&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UBvTM/btrEwzQjXa0/g1St4S6IJh5LmGYMlEOAzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UBvTM/btrEwzQjXa0/g1St4S6IJh5LmGYMlEOAzK/img.png&quot; data-alt=&quot;SSG닷컴 초기 UI&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UBvTM/btrEwzQjXa0/g1St4S6IJh5LmGYMlEOAzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUBvTM%2FbtrEwzQjXa0%2Fg1St4S6IJh5LmGYMlEOAzK%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;671&quot; height=&quot;463&quot; data-origin-width=&quot;671&quot; data-origin-height=&quot;463&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SSG닷컴 초기 UI&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;&amp;nbsp;위 그림은 SSG닷컴의 초기 UI이다. 이 UI를 통해 우리는 서비스가 신세계 계열사들의 정체성을 뽐내기 위해 안간힘을 쓰고 있었다는 것을 알 수 있다. 소비자는 이 포탈에 물건을 구매하러 접속한다. JAJU의 숟가락이 이마트몰에서 오든 신세계백화점몰에서 오든 아무 상관이 없다. 두 계열사에 따라 숟가락의 품질에 차이가 있는것은 아니기 때문이다. 소비자 입장에서는 이게 어디서 오든 그냥 나한테 '빨리' 오는게 더 이득이다.&lt;/p&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;롯데와 신세계는 뒤늦게 이커머스 시장에서의 경쟁력을 확보하기 위해 롯데온과 SSG닷컴을 설립했다. 새로운 계열사는 애자일 문화를 기반으로 한 디지털 조직을 내재화한 채로 출범했다. 이들에게는 더 이상 롯데정보통신과 신세계I&amp;amp;C 의 도움이 필요하지 않다. 이렇게 되면 IT서비스기업은 억울할 수도 있다. 마치 자신들 때문에 고객사가 디지털 플랫폼 시장에서 뒤쳐졌다는 것처럼 보이기 때문이다. 이는 반은 맞고 반은 틀리다.&lt;/p&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;IT서비스 기업이 자신이 디지털 시장에서 선택받지 못한 원인을 시장선점 실패의 '책임소재' 에서 찾는다면, 이들의 미래는 없을 것이다. 이들은 정치적인 이유였든, 자신들의 경쟁력이 이유였든 간에 새로운 시장에서 스스로의 필요성을 입증하지 못했다. 이들은 자신의 시장경쟁력이 빅테크 기업보다 가치있다는 사실을 실력으로 보여주었어야 했다. 그러나 이들은 그렇게 하지 못했고, 디지털 플랫폼 시장에서의 기회를 잃었다.&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;디지털 역량 내재화가 필요한 곳은 바로 IT서비스 기업이다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;전 산업군의 디지털 트랜스포메이션은 거스를 수 없는 대세이다. 이를 위해 IT서비스 기업들은 고객사의 DT 사업을 주도하기 위해 안간힘을 쓰고 있다. 그런데 무엇인가 이상하다. 빅테크 기업들과의 경쟁을 시작한 산업군에서는 IT서비스 기업을 점점 찾아볼 수 없다. 고객사는 비용과 리스크를 감수하고서라도 IT서비스 기업에 의존하는 방법보다 디지털 역량 내재화를 선택하고 있다. 롯데온과 SSG닷컴이 그랬고, CJ올리브영이 그랬다. 최근에는 LG U+가 디지털 역량 내재화를 발표했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1655026049563&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;'코딩'으로만 개발자 뽑는다... 올리브영 채용팀장 &amp;ldquo;기준은 도전정신&amp;rdquo;&quot; data-og-description=&quot;&amp;ldquo;의아할지도 몰라요. 올리브영이 개발까지 하느냐고요. 하지만 개발자가 온&amp;middot;오프라인을 아우르는 독보적인 커리어를 쌓을 기회라고 자신합니다.&amp;rdquo; 올리브영이 &amp;lsquo;개발자&amp;rsquo;를 뽑는다. 정보기&quot; data-og-host=&quot;n.news.naver.com&quot; data-og-source-url=&quot;https://n.news.naver.com/mnews/article/293/0000035825?sid=105&quot; data-og-url=&quot;https://n.news.naver.com/mnews/article/293/0000035825?sid=105&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/c9UinA/hyOJzepBcK/tuIWkWYmiS3g2XMJdVRF51/img.jpg?width=800&amp;amp;height=533&amp;amp;face=236_126_351_252,https://scrap.kakaocdn.net/dn/bTkfII/hyOJDumZKq/i1ozihVpYq2rpP1tktIVMK/img.jpg?width=800&amp;amp;height=533&amp;amp;face=236_126_351_252&quot;&gt;&lt;a href=&quot;https://n.news.naver.com/mnews/article/293/0000035825?sid=105&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://n.news.naver.com/mnews/article/293/0000035825?sid=105&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/c9UinA/hyOJzepBcK/tuIWkWYmiS3g2XMJdVRF51/img.jpg?width=800&amp;amp;height=533&amp;amp;face=236_126_351_252,https://scrap.kakaocdn.net/dn/bTkfII/hyOJDumZKq/i1ozihVpYq2rpP1tktIVMK/img.jpg?width=800&amp;amp;height=533&amp;amp;face=236_126_351_252');&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;'코딩'으로만 개발자 뽑는다... 올리브영 채용팀장 &amp;ldquo;기준은 도전정신&amp;rdquo;&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;의아할지도 몰라요. 올리브영이 개발까지 하느냐고요. 하지만 개발자가 온&amp;middot;오프라인을 아우르는 독보적인 커리어를 쌓을 기회라고 자신합니다.&amp;rdquo; 올리브영이 &amp;lsquo;개발자&amp;rsquo;를 뽑는다. 정보기&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;n.news.naver.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1655026115795&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;LGU+ 'CDO' 조직의 KPI가 매출 아닌 '조직 변화'인 이유&quot; data-og-description=&quot;&amp;ldquo;CDO(최고데이터책임자)로서 LG유플러스에 와서 인상깊었던 점은 경영진 전체가 데이터와 인공지능(AI)에 기반한 변화의 필요성을 절실히 느끼고 있었다는 것이다. 다른 사업 조직들도 마찬가지&quot; data-og-host=&quot;n.news.naver.com&quot; data-og-source-url=&quot;https://n.news.naver.com/mnews/article/293/0000039357?sid=105&quot; data-og-url=&quot;https://n.news.naver.com/mnews/article/293/0000039357?sid=105&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/HdQ0g/hyOJDgOTf4/RvB5UZ36pW8dN4LUWqRMn1/img.jpg?width=800&amp;amp;height=407&amp;amp;face=0_0_800_407,https://scrap.kakaocdn.net/dn/Y89Pk/hyOJtL3aHE/TjrY4hnUK208oNKLrjmHrk/img.jpg?width=800&amp;amp;height=407&amp;amp;face=0_0_800_407&quot;&gt;&lt;a href=&quot;https://n.news.naver.com/mnews/article/293/0000039357?sid=105&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://n.news.naver.com/mnews/article/293/0000039357?sid=105&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/HdQ0g/hyOJDgOTf4/RvB5UZ36pW8dN4LUWqRMn1/img.jpg?width=800&amp;amp;height=407&amp;amp;face=0_0_800_407,https://scrap.kakaocdn.net/dn/Y89Pk/hyOJtL3aHE/TjrY4hnUK208oNKLrjmHrk/img.jpg?width=800&amp;amp;height=407&amp;amp;face=0_0_800_407');&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;LGU+ 'CDO' 조직의 KPI가 매출 아닌 '조직 변화'인 이유&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;CDO(최고데이터책임자)로서 LG유플러스에 와서 인상깊었던 점은 경영진 전체가 데이터와 인공지능(AI)에 기반한 변화의 필요성을 절실히 느끼고 있었다는 것이다. 다른 사업 조직들도 마찬가지&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;n.news.naver.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;사실 지금 디지털 내재화 역량이 가장 필요한 곳은 IT서비스업계다. 앞선 포스팅에서 진단한 바와 같이 IT서비스 기업은 오랜 기간에 걸쳐 그 정체성을 잃어왔다. 이들의 서비스는 고객보다는 갑을 향하고 있으며, 이들의 주 업무는 비효율적 의사결정 구조에서 비롯한 비즈니스 오퍼레이션이다. 시장에서의 위기를 직감한 고객사는 더 이상 이들이 제공하는 '광팔이' 와 '책임소재' 가 필요하지 않다.&lt;/p&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;IT서비스 기업은 현재의 문화와 역량으로는 경쟁력이 없다는 불편한 진실을 인정해야 한다. 그리고 근본적인 변화를 시작해야 한다. 자신들의 현재까지의 사업에서 본질을 벗어나는 '아이러니' 를 찾아서 제거해야 한다. 이렇게 얻은 효율을 자신들에게 재투자 하여 새롭게 거듭나야 한다. 같은 계열사의 로고가 아닌 실력으로 자신들의 경쟁력을 입증해, 고객이 디지털 역량 내재화를 선택하는 것보다 자신들과의 파트너십을 구축하는게 더 효율적이라는 것을 보여주어야만 한다.&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;금융IT 업계는 어떻게 살아남을 수 있을까&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;금융IT 업계는 '아직은' 전통적인 외주기반 IT거버넌스 체계를 유지하고 있다. KB국민은행은 이번 코어뱅킹 현대화 사업 발표를 통해 디지털 내재화의 신호탄을 쐈다. 현재 산업의 거버넌스를 유지하고 있는 금융IT업계 종사자들은 타 산업군의 사례를 교훈삼아 자신들의 시장경쟁력에 대해 재고해야 한다. 이는 비단 금융IT 서비스 기업에만 해당되지 않는다. 금융IT 산업의 비효율성은 이를 구성하는 모든 조직에 의해 양산되었다. 현업 업무 담당자부터 n차 협력사의 개발자까지 자신이 현재 디지털 플랫폼 경쟁력을 가지고 있는지 냉정하게 돌아보아야 한다.&lt;/p&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;산업의 축이 디지털을 기반으로 재편되면서 금융사의 IT부서 혹은 IT계열사 종사자들의 역할이 중요해졌다. 이제는 자신들이 지원부서 직원이라는 수동적인 생각을 버려야 한다. 이들은 타 조직을 이끌어나가며 지속가능한 서비스에 대한 고민을 시작해야 한다. 지속가능한 서비스는 '끊임없는 개선' 에서 시작한다. 처음부터 완벽한 시스템은 존재하지 않는다. 불필요한 프로세스와 결함을 찾아 선제적으로 개선하여, 새로운 비즈니스를 시작할 타이밍을 놓치지 않도록 평소에 준비해야 한다. 금융산업에서 잦은 변화는 리스크를 동반한다. 이 리스크를 줄이기 위해 아무것도 하지 않는 방법은 이제 더 이상 시장에서 통하지 않는다. 정책과 업무 프로세스를 분석해 불필요한 절차를 과감히 없애고, 이를 통해 얻은 효율을 자신의 역량 향상에 투자해야 한다. 이들은 기획부서와 외주사를 아울러 최적의 의사결정을 내려야 한다. 필요하다면 현업부서를 선제적으로 설득해 움직여야 하고, 외주사를 움직일 수 있어야 한다. 금융IT 담당자로서 금융과 IT를 모두 잘 해야 하는 올라운더의 의무를 지고 있다. 결재만 잘해서는 미래를 보장할 수 없다.&lt;/p&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;IT서비스사는 자신들의 본질이 어디에 있는지 고민해야 한다. 이들은 디지털 플랫폼 시장에서 빅테크와 직접적으로 경쟁해야 하는 의무를 가지고 있다. 먼저 자신의 역량에 대해 냉정한 판단을 내려야 한다. IT서비스 기업의 평균적인 역량은 빅테크에 비해 한참 떨어진다. '업무지식은 우리가 나아' 와 같은 일말의 안일한 인식도 버려야 한다. 이제 온라인 쇼핑은 네이버와 쿠팡이 롯데와 신세계보다 '업무적' 으로도 더 낫다. 역량의 차이가 벌어질 수 밖에 없는 이유는 이들의 사업비전에 있다. IT서비스 기업은 소프트웨어를 '공산품' 과 같이 취급한다. 시스템의 오픈을 목표로 Man/Month 를 기반으로 하는 일괄적인 양산체계로 찍어낸다. 빅테크 기업은 소프트웨어를 '프로덕트' 로 표현한다. 시스템의 오픈을 넘어 지속적으로 개선해야 할 서비스로 보고 이를 위해 조직의 역량을 개선시킬 투자를 감행한다.&lt;/p&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;기능실행을 목적으로 하는 공산품과, 더 좋은 서비스 출시를 위해 진화하는 프로덕트 라는 비전의 차이는, 두 업계의 개발자 역량에 엄청난 차이를 불러왔다. IT서비스 기업에서는 개발자의 성장을 위한 문화를 찾아볼 수 없다. 기술공부를 하는 행위를 이상하게 생각하며 심지어는 '유난떤다' 라는 표현도 서슴치 않는다. '그 시간에 업무를 공부해라' 라고 훈수를 두시는 분들도 있다. 빅테크 기업은 개발자의 역량을 향상하기 위해 전사적 자원을 집중한다. 학습을 자사 비즈니스의 한 축으로 둔다. 결과적으로 이는 '투자' 효과를 일으켰다. 이를 통해 얻어진 구성원들의 역량과 효율은 기술적, 업무적 성장을 이끌었고 빅테크와 IT서비스 기업간의 절대적 우위로 나타났다. IT서비스사는 자신들의 산출물이 현재 시장이 요구하는 프로덕트가 맞는지 진지하게 고민해보고, 빅테크와의 비교열위를 극복하기 위해 '문화' 를 기반으로 하는 조직개편 작업을 시작해야 한다.&lt;/p&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;마지막으로 업계의 개발자들은 자신의 역량이 디지털 네이티브 기업에서 활용될 수 있을지 고민해야 한다. 앞선 포스팅에서 국민은행의 새로운 채용공고를 살펴본 바 있다. 이 공고는 '클라우드 환경에서의 업무경력 3년' 을 필수 요건으로 내걸었다. 이 글을 보고 읽는 사람이 일반적인 IT기업의 개발자라면 별 의미없는 조건이라고 생각할 수 있을것이다. 하지만 이 글을 읽은 금융IT 개발자들 중에 저 자격요건을 가지고 웃어넘길 수 있는 코어뱅킹 개발자가 몇이나 될까?&lt;/p&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;오랜 기간에 걸쳐 금융IT 거버넌스 체계가 퇴보했다는 사실은 앞서 여러번 언급했다. 그 정도는 예상보다 더 심각하다. 대다수의 금융IT 서비스 기업의 개발자와 프리랜서들은 프로그래밍을 '조건문을 추가하는 일' 정도로 여긴다. '뭐 개발이 다 똑같지. 그냥 if문만 계속 추가하는거지 뭐' 라고 말하는 사람들이 대다수다. 심지어 간단한 문자열 비교나 자르기도 어려워 하는 사람들도 많다. '기술보다는 업무가 중요하다' 라는 문화속에서 그저 돌아가기만 하면 되는 시스템을 구현하느라 기술의 발전을 등한시한 결과다. 그래서 금융IT 업계에는 '키맨' 을 찾아볼 수 있다. 적은 수의 키맨들의 희생에 의해 시스템은 동작하고 있었다. 최근 개발자의 처우가 비약적으로 향상되면서 이 키맨들은 자신들의 가치를 인정해주는 곳을 찾아 시장을 점점 떠나고 있다. 고객은 키맨이 없는 IT서비스 회사의 도움이 필요하지 않아졌다. 다른 시장의 개발자 풀에서 자신의 미래를 찾기 시작했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;개발자들은 지금이라도 자신의 역량을 향상하기 위한 학습을 시작해야 한다. 혼자 하기 어렵다면 팀원을 설득해서 함께 나아가야 한다. 학습은 기본소양을 갖춰준다. 이것만으로는 시장에서 요구하는 수준을 달성하기 힘들다. 실무경험을 통해 자신의 역량향상을 경력으로 녹여야 한다. 이를 위해 소속회사와 의사결정자를 설득해야 한다. 분명 변화를 거부하는 저항에 부딪힐 것이다. 하지만 시간은 기다려주지 않는다. 머지 않은 시일 안에 훨씬 뛰어난 역량을 가진 개발자들과 경쟁해야 한다. 작은 것부터 보여주어야 한다. 자신의 기술적 역량이 비즈니스에 도움을 주어 소속회사와 고객사의 이익에 기여하는 선순환을 지속한다면, 신뢰를 얻을 수 있다. 이 신뢰를 바탕으로 자신의 성장을 위한 정책을 업무의 일부로 요구할 수 있을 것이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;정책이 아니라 문화다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;금융IT 업계가 살아남기 위해서는 여러 이해관계를 가진 다양한 조직이 합심해 시장에서의 경쟁력을 입증해야 한다. 이들을 한데 묶을 수 있는 고리는 문화로부터 시작해야 한다. 기존의 획일적이고 수직적인 정책기반의 의사결정 구조로는 시장의 복잡한 요구에 신속하게 대응할 수 없다.&lt;/p&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;먼저 공급자 중심의 개념부터 바꿔야 한다. 'IT부서에서 만든 단말' 이라는 '전산' 개념을 '고객에게 전달해야 할 차별화된 경험' 이라는 뜻의&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;그간 금융IT 구성조직 사이에는 서로에 대한 불신이 팽배했다. 각 조직이 왜곡된 본질에서 서로 다른 목표를 향하고 있었기 때문이다. 이제 '책임소재' 를 가지고 다투는 일을 멈춰야 한다. 서로의 장점을 살려 긴밀하게 협업하지 않으면 디지털 플랫폼 시장에서 공멸할 수 있다는 위기의식을 가져야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;조직을 하나로 묶는 것은 하향식의 정책이 아니라, 구성원들의 자발적인 참여로 형성되는 문화이다. 문화는 공감에서부터 시작된다. 공감은 지속적인 커뮤니케이션을 통해 얻을 수 있다. 요즘 주목받는 '애자일' 조직이 갖는 특징이 여기에 있다. 애자일 조직은 짧은 스프린트를 주기로 지속적인 커뮤니케이션을 유지한다. 이렇게 일하는 방식은 조직 구성원들 사이의 공감을 기반으로 하는 문화를 형성한다. '애자일' 이라는 트렌디한 단어에 현혹되어 스프린트 프로세스를 정책으로 구성해 강제로 적용하는 것은 실패할 확률이 높다. 구성원들의 사고방식과 조직의 문화를 어떻게 변화시킬 지 고민하다보면 '애자일' 은 자연스럽게 따라올 것이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;정책의 한계를 돌파하는 기업이 시장을 선도할 것이다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;금융산업은 국가의 정책을 기반으로 움직이는 규제산업이라는 점에서 타 산업군과 큰 차이가 있다. 이 때문에 시장을 선도하기 위해서는 기업의 자체경쟁력과 더불어 정책을 대하는 관점에서의 차별화된 전략이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;정책을 해석하는 시선을 넘어 정책을 주도하는 순간, 시장의 주도자가 될 수 있는 기회를 열 수도 있다. 토스 서비스를 운영하고 있는 비바리퍼블리카의 이승건 대표는 2014년부터 토스의 간편송금 서비스를 내놓기 위해 직접 금융당국과 은행을 찾아다니면서 관계자들을 설득했다. 토스는 오랜시간에 걸쳐 정책과 금융업계의 룰을 바꾸는데 성공했다. 이렇게 출시한 간편송금 기능은 토스를 플랫폼으로 만드는 '킬러 서비스' 의 역할을 톡톡히 수행했다. 토스는 오늘날 플랫폼으로써 토스뱅크, 토스증권을 아우르는 금융 슈퍼앱의 형태로 진화했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;필자는 보수적인 금융권에서 더 나은 서비스를 제공하기 위해 스스로 힘든 길을 걷고있는 토스를 응원한다. 그러나 살짝 안타까운 면이 있는것도 사실이다. 최근 토스는 보험설계사에게 개인정보를 판매했다는 논란에 휩싸인 바 있다. 사실 토스는 정책적으로 아무 잘못을 하지 않았다. 정책을 해석하고 고객에게 서비스로 전달하는 과정에서 임의의 판단을 내린 것이 문제였다. 토스는 고객에게 좋은 일이라는 이유로 자신들이 판단한 내용을 세심하게 알리지 않았다. 똑똑한 고객은 미묘한 표현의 오류에서 개인정보 판매라는 부정적 이슈를 제기했고, 이는 토스에게 큰 타격이 되었다. 토스가 고객의 입장을 조금만 더 생각했더라면 하는 아쉬움이 있다. 그러나 토스의 이러한 이슈는 고객을 향한 서비스 위에서 발생했다. 기존 금융사들이 일으키던 로그인 불가, 타임아웃과 같은 서비스 품질과 관련된 이슈와는 그 본질부터 다르다.&lt;/p&gt;
&lt;figure id=&quot;og_1655040299949&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[이슈진단+] [기자수첩] 토스 논란에 관한 팩트체크&quot; data-og-description=&quot;토스가 연일 시끄럽다. 토스가 보험설계사에게 개인 정보를 판매했다는 점이 보도되면서 토스를 더 이상 쓰지 않겠다는 '불매 운동'까지 거론되고 있다. 토스에 대한 논란은 자극적으로 포장되&quot; data-og-host=&quot;n.news.naver.com&quot; data-og-source-url=&quot;https://n.news.naver.com/mnews/article/092/0002259248?sid=105&quot; data-og-url=&quot;https://n.news.naver.com/mnews/article/092/0002259248?sid=105&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/byVGux/hyOJAq6ZLB/zdNHi08PKGLh3rQch2FpE1/img.jpg?width=640&amp;amp;height=164&amp;amp;face=0_0_640_164,https://scrap.kakaocdn.net/dn/VaPTJ/hyOJrVdCId/4tP1Lz30OHDK8UuqkggPVk/img.jpg?width=640&amp;amp;height=164&amp;amp;face=0_0_640_164&quot;&gt;&lt;a href=&quot;https://n.news.naver.com/mnews/article/092/0002259248?sid=105&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://n.news.naver.com/mnews/article/092/0002259248?sid=105&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/byVGux/hyOJAq6ZLB/zdNHi08PKGLh3rQch2FpE1/img.jpg?width=640&amp;amp;height=164&amp;amp;face=0_0_640_164,https://scrap.kakaocdn.net/dn/VaPTJ/hyOJrVdCId/4tP1Lz30OHDK8UuqkggPVk/img.jpg?width=640&amp;amp;height=164&amp;amp;face=0_0_640_164');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[이슈진단+] [기자수첩] 토스 논란에 관한 팩트체크&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;토스가 연일 시끄럽다. 토스가 보험설계사에게 개인 정보를 판매했다는 점이 보도되면서 토스를 더 이상 쓰지 않겠다는 '불매 운동'까지 거론되고 있다. 토스에 대한 논란은 자극적으로 포장되&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;n.news.naver.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;시장의 고객은 현명하고 냉정하다. 정책을 핑계삼아 아무것도 하지 않거나, 자신들의 잘못을 정책의 탓으로 돌리려는 기업을 고객이 이해해주어야 할 이유는 없다. 고객의 더 나은 삶을 위해 정책을 해석하고, 더 나아가 더 좋은 정책을 이끌어내려는 기업은 시장의 선택을 받을 수 있을 것이다.&lt;/p&gt;
&lt;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;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;지금까지 KB국민은행의 코어뱅킹 현대화 사업을 중심으로 금융IT 업계의 현재와 미래에 대한 필자의 생각을 적어보았다. 긴 포스팅에 걸쳐 여러 주제를 다루느라 중복되거나, 흐름을 벗어나는 이야기도 다수 포함한 것 같다. 필자의 주관적인 생각이 많이 들어간 만큼 다른 견해와 시선이 있을 수 있다. 또 이미 문제점을 인식해 더 나은 방향으로 나아가고 있는 기업들도 많을 것이다.&lt;/p&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>프로그래밍/금융IT</category>
      <category>계정계</category>
      <category>국민은행</category>
      <category>금융</category>
      <category>금융it</category>
      <category>디지털뱅킹</category>
      <category>차세대</category>
      <category>카카오뱅크</category>
      <category>코어뱅킹</category>
      <category>클라우드</category>
      <category>토스</category>
      <author>림딩동</author>
      <guid isPermaLink="true">https://limdingdong.tistory.com/27</guid>
      <comments>https://limdingdong.tistory.com/27#entry27comment</comments>
      <pubDate>Sun, 12 Jun 2022 17:59:14 +0900</pubDate>
    </item>
  </channel>
</rss>