<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>sour_grape.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Wed, 18 Feb 2026 07:33:55 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>sour_grape.log</title>
            <url>https://velog.velcdn.com/images/sour_grape/profile/24ca2aff-264f-43ef-b167-30bb99ada76c/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. sour_grape.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/sour_grape" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[TDD를 부숴주마 - setup과 구현 부산물]]></title>
            <link>https://velog.io/@sour_grape/TDD%EB%A5%BC-%EB%B6%80%EC%88%B4%EC%A3%BC%EB%A7%88-setup%EA%B3%BC-%EA%B5%AC%ED%98%84-%EB%B6%80%EC%82%B0%EB%AC%BC</link>
            <guid>https://velog.io/@sour_grape/TDD%EB%A5%BC-%EB%B6%80%EC%88%B4%EC%A3%BC%EB%A7%88-setup%EA%B3%BC-%EA%B5%AC%ED%98%84-%EB%B6%80%EC%82%B0%EB%AC%BC</guid>
            <pubDate>Wed, 18 Feb 2026 07:33:55 GMT</pubDate>
            <description><![CDATA[<h1 id="구현-부산물">구현 부산물</h1>
<p>assert문을 제외한 부분은 단위 테스트에서 setup이다.</p>
<p>이 부분은 구현 부산물이라서 의미론적으론 테스트에서 중요하지 않다.</p>
<p>하지만 테스트를 작성 할 때 신경써야하는 부분이 setup 과정이다.</p>
<p>흔히 말하는 flaky test의 경우 setup 과정의 문제이기 때문에 구현에 신경써야한다.</p>
<p>위 setup 과정은 깨지는 이유가 너무 다양하고 필자는 QA도 아니여서 글이 미숙할 수 있다.</p>
<p>그래서 얕고 주관이 많이 들어간 setup 방법을 설명하겠다.</p>
<h1 id="flaky-test">flaky test</h1>
<p>테스트에서 flaky test는 일관된 성공, 일관된 테스트의 실패가 아닌<br>어쩔 땐 성공, 어쩔 땐 테스트 실패로 불안정한 테스트를 말한다.</p>
<p>flaky test는 이런 특징 때문에 개발자를 개빡치게 만드는데<br>별 의미가 없는 곳에 디버깅과 프레임워크의 내부 구현에 신경써야되는등 시간을 죽일 뿐더러<br>환경이 달라지면 테스트가 깨지는 등 테스트가 계약 검증의 역할을 못하게 한다.</p>
<p>그래서 테스트 setup의 경우엔 비용을 좀 더 지불하더라도 안정성 있는 테스트를 작성하는게 심신에 좋다.</p>
<p>flaky test가 되는 이유는 너무도 다양하다. 사실 나도 모든 경우를 잘 알지 못한다.<br>하지만 대표적으로 문제가 되는 지점을 살펴보겠다.</p>
<h2 id="db-io">DB IO</h2>
<pre><code class="language-java">// DB (공유 스키마/잔여 데이터)
// setup에서 &quot;기존 데이터가 비어있다&quot;를 가정하면, 다른 테스트/이전 실행 잔여로 랜덤 깨짐
// 실제 DB랑 연결 안 되면 테스트 깨짐
repo.save(user);
assertEquals(1, repo.count()); // 다른 테스트가 먼저 넣어놨으면 2</code></pre>
<h2 id="네트워크-io">네트워크 IO</h2>
<pre><code class="language-java">// 네트워크(외부 API)
// 레이트리밋/타임아웃/503/DNS 같은 &quot;환경 노이즈&quot;가 곧 flaky
String body = http.get(&quot;https://api.somewhere.com&quot;);
assertTrue(body.contains(&quot;OK&quot;)); // 가끔 429/timeout 나면 끝</code></pre>
<h2 id="파일-io">파일 IO</h2>
<pre><code class="language-java">// 파일 IO(고정 경로)
// build/tmp 같은 공유 경로 쓰면 병렬/재시도에서 덮어쓰기/삭제로 랜덤 깨짐
Files.writeString(Paths.get(&quot;build/tmp/out.txt&quot;), &quot;x&quot;);
assertEquals(&quot;x&quot;, Files.readString(Paths.get(&quot;build/tmp/out.txt&quot;))); // 다른 테스트가 바꾸면 깨짐</code></pre>
<p>IO 같은 경우는 통신이 안 되면 테스트가 깨지니까 취약점이라고 생각하면 된다.</p>
<h2 id="시간">시간</h2>
<pre><code class="language-java">// 시간(now) / 날짜 경계
// now()를 그대로 쓰면 &quot;자정/타임존/DST 경계&quot;에서 랜덤 깨짐
LocalDate d = LocalDate.now();
assertEquals(LocalDate.of(2026, 2, 18), d); // 날짜 바뀌는 순간 깨짐</code></pre>
<p>시간을 다루는 건 빡세서 주의 깊은 테크닉을 사용해야 한다.</p>
<h2 id="비동기">비동기</h2>
<pre><code class="language-java">// sleep 기반 동기화(비동기)
// sleep은 완료 조건이 아니라 희망사항이라 CI에서 자주 깨짐
asyncJob.start();
Thread.sleep(10);
assertTrue(asyncJob.isDone()); // 머신 느리면 false</code></pre>
<h2 id="전역-상태">전역 상태</h2>
<pre><code class="language-java">// 전역 상태(System property / singleton / static cache)
// setup이 전역을 건드리면 다음 테스트에 독 묻혀서 순서 의존 발생
System.setProperty(&quot;MODE&quot;, &quot;A&quot;);
// ... 원복 안 함
assertEquals(&quot;A&quot;, System.getProperty(&quot;MODE&quot;)); // 다음 테스트가 MODE=B로 바꾸면 랜덤</code></pre>
<h2 id="랜덤">랜덤</h2>
<pre><code class="language-java">// 랜덤/UUID(Seed 미고정)
// 재현 불가 + 희귀 케이스가 가끔 튀어서 랜덤 실패
int n = new Random().nextInt(10);
assertNotEquals(7, n); // 10번 중 1번 깨짐</code></pre>
<h2 id="컬렉션">컬렉션</h2>
<pre><code class="language-java">// 컬렉션 순서(HashMap/HashSet)
// 순서 보장 없는데 순서로 assert하면 JVM/버전/해시 시드에 따라 깨짐
List&lt;String&gt; keys =
    new ArrayList&lt;&gt;(new HashMap&lt;&gt;(Map.of(&quot;a&quot;, 1, &quot;b&quot;, 2)).keySet());

assertEquals(List.of(&quot;a&quot;, &quot;b&quot;), keys); // 순서 안 맞으면 깨짐</code></pre>
<h2 id="프레임워크">프레임워크</h2>
<pre><code class="language-java">// 프레임워크 컨텍스트 공유(Spring 캐시/싱글톤 상태)
// 테스트가 같은 컨텍스트/빈을 공유하고 상태가 남으면 조합에 따라 랜덤 실패
service.enableFeatureX(); // 싱글톤 상태 변경
assertTrue(service.isFeatureXEnabled()); // 다음 테스트가 disable 하면 랜덤</code></pre>
<h1 id="mock-stub-fake">mock, stub, fake</h1>
<p>이 녀석들은 이렇게 알아두면 된다.<br>&quot;일단 된다고 쳐&quot;</p>
<p>단위 테스트에서 중요하지 않은 부분인데 깨지기 쉬운 취약점이고 비용이 많이 든다면 대부분 mock/stub/fake를 이용한다.</p>
<p>mock/stub/fake는 보통 IO 경계에서 쓴다.<br>왜냐하면 우리가 비지니스 클래스를 만들고 테스트하는데 이 동작들이 IO를 하는 경우가 많기 때문이다.</p>
<p>네트워크 통신과 DB와의 통신은 말할 것도 없고 시스템과 IO하는 과정도 빈번하게 일어난다.</p>
<p>이 지점은 계약 검증에 아무 의미도 없고 단위 테스트가 아니라 통합 테스트의 책임이며 무엇보다 테스트가 깨지기 쉬운 지점이다.</p>
<p>이렇게 별 의미없고 깨지기 쉽고 비용도 많이드는 부분은 mock/stub/fake 처리를 해준다.</p>
<h2 id="stub">stub</h2>
<pre><code class="language-java">// 1) STUB: 관심 없음. 그냥 값만 주고 테스트 진행시키기
ShippingFeeClient shipping = req -&gt; 3000; // 무조건 3000원 나온다고 쳐
OrderService sut = new OrderService(shipping /* ... */);
assertEquals(13000, sut.totalPrice(10000)); // 배송비 포함 로직만 확인</code></pre>
<hr>
<h2 id="fake">fake</h2>
<pre><code class="language-java">// 2) FAKE: 진짜처럼 동작하는 인메모리 구현(상태 기반으로 안정적)

class InMemoryUserRepository implements UserRepository {
    private final Map&lt;String, User&gt; store = new HashMap&lt;&gt;();

    @Override public void save(User user) { store.put(user.name(), user); }
    @Override public boolean exists(String name) { return store.containsKey(name); }
}


UserRepository repo = new InMemoryUserRepository(); // Map으로 save/find 구현했다고 쳐
OrderService sut2 = new OrderService(repo /* ... */);
sut2.registerUser(&quot;kim&quot;);
assertTrue(repo.exists(&quot;kim&quot;)); // 상태로 검증(결정적이라 잘 안 깨짐)</code></pre>
<hr>
<h2 id="mock">mock</h2>
<pre><code class="language-java">// 3) MOCK: &quot;호출했는지&quot; 자체가 계약일 때만 씀(상호작용 검증)
EmailSender email = Mockito.mock(EmailSender.class);
OrderService sut3 = new OrderService(email /* ... */);
sut3.completeOrder(&quot;a@b.com&quot;);
Mockito.verify(email).send(&quot;a@b.com&quot;, &quot;ORDER_COMPLETE&quot;); // 호출/인자 검증</code></pre>
<hr>
<h1 id="opt-in">opt-in</h1>
<p>flaky test 중 비번하게 일어나는 부분이 바로 전역 상태와 프레임워크 때문에 깨지는 거다.</p>
<p>우리가 IO나 time, random 같은 경우를 올바른 테크닉으로 처리하더라도 테스트가 터지는데<br>대게 전역 상태와 프레임워크 때문에 터지는 경우이다.</p>
<p>이 경우 디버깅이 매우 어려워지는데<br>디버깅을 위해서 setup 전역 상태 초기화와 tear-down의 순서를 신경써야하고<br>공유하고 있는 모든 테스트의 협력 관계를 유심히 디버깅 해야한다.</p>
<p>프레임워크 때문에 깨지는 경우는 더 최악인데 spring test 같은 경우<br>디버깅을 위해서 프레임워크 내부의 구현을 공부해서 디버깅 해야된다.</p>
<p>그리고 깨지기도 정말 많이 깨져서 나는 스프링부트 테스트(@SpringBootTest)는 사용 안 하는걸 권한다.</p>
<p>잡설이 길었는데 이걸 해결하는게 opt-in 방식이다.</p>
<h2 id="1-테스트당-격리">1. 테스트당 격리</h2>
<p>이렇게 적지말고(공유 fixture) 비용이 더 지불해도 전역 상태 대신 테스트당 하나씩 만들어라.</p>
<pre><code class="language-java">// BAD: 공유 fixture (테스트 순서/병렬에서 언젠가 터짐)
static final InMemoryUserRepository SHARED = new InMemoryUserRepository();

@Test
void bad_shared_fixture() {
    SHARED.save(&quot;kim&quot;);
    assertTrue(SHARED.exists(&quot;kim&quot;)); // 다른 테스트가 clear 하면 랜덤 깨짐
}</code></pre>
<pre><code class="language-java">@Test
void unit_test_isolation_example() {
    // 테스트마다 새로 만들어서(격리) 상태를 공유하지 않음
    InMemoryUserRepository repo = new InMemoryUserRepository();
    FakeClock clock = new FakeClock(&quot;2026-02-18T00:00:00Z&quot;);

    UserService sut = new UserService(repo, clock);

    sut.register(&quot;kim&quot;);
    assertTrue(repo.exists(&quot;kim&quot;));
}</code></pre>
<h2 id="2-명시적인-표시">2. 명시적인 표시</h2>
<p>터지기 쉬운 테스트를 다뤄야 할 땐 기본(unit) 스위트에 섞지 말고,<br>명시적으로 켰을 때만 돌게 만든다.</p>
<pre><code class="language-java">@Tag(&quot;optin&quot;)
@Test
void spring_boot_test_like_this() { }</code></pre>
<p>이런식으로 프레임워크가 제공하는 기능을 이용하던 폴더를 분리하던<br>위험한 테스트는 따로 격리한다.</p>
<p>CI 과정에서 테스트를 통과를 해야하는데 그때마다 위험한 테스트가 계속 깨지면 신뢰성이 박살이라<br>위험한 테스트는 따로 필요할 때만 테스트 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TDD를 부숴주마 - 테스트와 TDD에 대한 오해]]></title>
            <link>https://velog.io/@sour_grape/TDD%EB%A5%BC-%EB%B6%80%EC%88%B4%EC%A3%BC%EB%A7%88-%ED%85%8C%EC%8A%A4%ED%8A%B8%EC%99%80-TDD%EC%97%90-%EB%8C%80%ED%95%9C-%EC%98%A4%ED%95%B4</link>
            <guid>https://velog.io/@sour_grape/TDD%EB%A5%BC-%EB%B6%80%EC%88%B4%EC%A3%BC%EB%A7%88-%ED%85%8C%EC%8A%A4%ED%8A%B8%EC%99%80-TDD%EC%97%90-%EB%8C%80%ED%95%9C-%EC%98%A4%ED%95%B4</guid>
            <pubDate>Sun, 15 Feb 2026 08:41:46 GMT</pubDate>
            <description><![CDATA[<h1 id="오늘도-봤다">오늘도 봤다</h1>
<p>벨로그에서 글을 쓰기 전에 인기글을 읽는 편이다. 근데 회고를 보는데 TDD 얘기가 있더라.</p>
<p>개발 블로그 글 볼 때 TDD는 진짜 하루에 한 번씩은 보는 것 같다.</p>
<p>그때마다 열 받는다. 오늘도 벨로그 글은 안 쓰려고 했는데 TDD 글을 봐서 열 받아서 쓴다.</p>
<h1 id="테스트에-대한-오해">테스트에 대한 오해</h1>
<h2 id="테스트는-코드-품질과-전혀-관계가-없다">테스트는 코드 품질과 전혀 관계가 없다</h2>
<p>테스트 목적은 계약을 만족시켰는지 검증하는 거지 코드 품질을 측정하는 게 아니다.</p>
<p>내가 말하는 코드 품질은 이런 거다.</p>
<ul>
<li>불변성</li>
<li>리스코프 치환 원칙</li>
<li>OCP</li>
<li>의존성 관리</li>
<li>객체지향적인 설계에서의 결합도/응집도</li>
<li>변경 비용</li>
</ul>
<p>근데 테스트가 위 품질 지표 중에서 뭘 검증해주냐? 아무것도 못 한다.</p>
<p>테스트가 통과된 건 구현체가 계약을 만족했다는 뜻이지, 객체지향을 만족했다는 게 아니다.</p>
<h2 id="테스트는-버그를-잡아주지-않는다">테스트는 버그를 잡아주지 않는다</h2>
<p>예측할 수 있다면 버그가 아니다.</p>
<p>결국 테스트는 사람이 짠 계약을 검증하는 건데, 이 계약은 사람이 생각난 걸 적은 것이다. 사람이 생각하지 못한 건 커버 못 한다.</p>
<p>버그의 대부분은 이런 예상치 못한 곳에서 터진다.</p>
<ul>
<li>운영 환경</li>
<li>동시성</li>
<li>GC</li>
<li>메모리 누수</li>
<li>IO 누수</li>
<li>배포 설정</li>
<li>외부 시스템 계약 변화</li>
</ul>
<p>테스트 몇 개 더 쓴다고 이런 게 원천적으로 예방되는 게 아니다.</p>
<h2 id="테스트는-설계와-관계가-없다">테스트는 설계와 관계가 없다</h2>
<p>설계를 잘하고 그 계약을 테스트 하는 것이다.</p>
<p>애초에 설계가 잘못 돼서 계약이 잘못 나오고, 그걸 테스트해서 통과하더라도 잘못된 설계임을 알지 못한다.</p>
<p>내가 뼈저리게 느낀 점은 이거다.</p>
<p>설계가 좋든 안 좋든 상관없이 테스트는 통과한다.</p>
<h2 id="커버리지가-높아도-버그를-잡지-못한다">커버리지가 높아도 버그를 잡지 못한다</h2>
<p>나도 상용 프레임워크를 개발하면서 엄격함을 위해 Jacoco 기준으로 분기 커버리지를 95% 이상으로 올린 적이 있다.</p>
<p>근데 과연 치명적인 버그를 예방했냐? 전혀 아니다.</p>
<p>운영에서 터지는 치명적인 결함, 메모리 누수, 내 의도와는 반대된 동작은 운영 환경과 언어 특성, 컴파일, 런타임 같은 걸 알고 막아야 한다.</p>
<p>커버리지는 계약을 커버했다는 지표일 뿐이다.</p>
<p>나도 커버리지 높다고 안심하다가 설계를 전체적으로 갈아엎는 일이 발생했다.</p>
<p>애초에 설계를 잘못했고, 버그를 못 피하는 운명으로 시작하면 커버리지가 아무리 높아도 버그를 못 잡는다.</p>
<h2 id="테스트는-리팩토링을-도와주지-않는다">테스트는 리팩토링을 도와주지 않는다</h2>
<p>테스트가 리팩토링을 도와주는 경우는 딱 하나다.</p>
<p>리팩토링 한 구현체가 여전히 계약을 만족했는지 검증하는 용도.</p>
<p>리팩토링은 시스템이 성장하면서 책임을 명확하게 하고 분리하고, 최적화를 하는 작업이다.</p>
<p>근데 그 리팩토링 요소에 테스트가 직접적으로 도움을 주는 건 없다.</p>
<h2 id="테스트는-최적화와-전혀-관계가-없다">테스트는 최적화와 전혀 관계가 없다</h2>
<p>최적화는 정해진 계약을 만족하는 걸 전제로, 시스템에 주어진 상황을 판단해서 일관적인 트레이드오프를 고르는 일이다.</p>
<p>최적화를 논의한다는 건 이미 뭘 버리고 뭘 취할지 고민하는 지점, 그러니까 pareto-point 근처까지는 왔다는 뜻이다.</p>
<p>근데 여기까지 도달하는 데 테스트가 도움을 주냐? 아무 도움도 못 준다.</p>
<p>최적화는 테스트가 아니라 관측, 데이터, 프로파일링의 영역이다.</p>
<h2 id="테스트는-객체지향-디자인패턴과-관련이-없다">테스트는 객체지향, 디자인패턴과 관련이 없다</h2>
<p>이것도 마찬가지다.</p>
<p>객체지향적인 설계, 디자인패턴은 상황에 맞는 올바른 지식을 적용하는 능력이 필요하다.</p>
<p>테스트는 객체지향과 디자인 패턴을 적용해 리팩토링 했을 때 여전히 계약을 지켰는지만 확인해준다.</p>
<h2 id="테스트는-유지보수를-더-어렵게-한다">테스트는 유지보수를 더 어렵게 한다</h2>
<p>flaky test라는 게 있다. 깨지는 테스트.</p>
<p>근데 이게 계약을 만족시키지 못해서 깨지는 게 아니다.</p>
<p>외부 요인으로 인해 어떤 땐 성공하고 어떤 땐 실패하는 불안정한 상태를 의미한다.</p>
<p>이걸 해소하려면</p>
<ul>
<li>테스트 프레임워크 내부 동작을 알아야 하고</li>
<li>전역 상태 관리를 잘 해야 하고</li>
<li>외부 의존성을 주의 깊게 다뤄야 한다</li>
</ul>
<p>근데 이게 쉬운 일이 아니라서 테스트 유지보수 하면서 샷건치는 나를 발견하게 된다.</p>
<h3 id="자바-예시-스프링에서-mockbean이-컨텍스트-캐시-타고-새서-다음-테스트에-영향">자바 예시: 스프링에서 MockBean이 컨텍스트 캐시 타고 새서 다음 테스트에 영향</h3>
<p>스프링 테스트는 기본이 전역 컨텍스트 캐시 + 싱글톤이다.</p>
<p>그래서 테스트 A가 컨텍스트 안의 빈을 한 번이라도 @MockBean으로 갈아끼우면,
테스트 B는 자기 잘못 없어도 랜덤하게 터질 수 있다.</p>
<p>로컬에서는 운 좋게 안 터지다가 CI에서 순서가 바뀌거나 캐시 재사용 조건이 꼬이면 터진다.</p>
<pre><code class="language-java">import org.springframework.stereotype.Service;

@Service
public class PaymentGateway {
    public String pay() {
        return &quot;REAL&quot;;
    }
}</code></pre>
<pre><code class="language-java">import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;

@SpringBootTest
class TestA_with_mock {

    @MockBean
    PaymentGateway gateway;

    @Test
    void uses_mock() {
        when(gateway.pay()).thenReturn(&quot;MOCK&quot;);
        assertThat(gateway.pay()).isEqualTo(&quot;MOCK&quot;);
    }
}</code></pre>
<pre><code class="language-java">import static org.assertj.core.api.Assertions.*;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class TestB_expects_real {

    @Autowired
    PaymentGateway gateway;

    @Test
    void expects_real() {
        // 어떤 환경/순서/캐시 조건에서는 여기서 REAL이 아니라 MOCK이 남아있어서 터진다.
        // 내 코드가 잘못된 게 아니라, 전역 컨텍스트 + 싱글톤 + 캐시 재사용이 만들어내는 부작용이다.
        assertThat(gateway.pay()).isEqualTo(&quot;REAL&quot;);
    }
}</code></pre>
<p>여기서 더 빡치는 포인트는 이거다.</p>
<p>이런 테스트는 진짜 짜증나는 게, 실패가 재현이 일정하지 않다.
그래서 디버깅이 도메인 디버깅이 아니라 스프링 테스트 캐시/컨텍스트/빈 교체 규칙 디버깅이 된다.</p>
<p>결국 테스트 유지보수가 코드 유지보수보다 더 비싸지는 순간이 오고,
그때부터 테스트는 안전망이 아니라 지뢰밭이 된다.</p>
<h2 id="테스트는-디버깅을-단축-시켜준다고-하지만-테스트-디버깅에-더-시간듬">테스트는 디버깅을 단축 시켜준다고 하지만 테스트 디버깅에 더 시간듬</h2>
<p>구현체의 계약을 쪼개서 검증하기 때문에 쪼개진 구현체 부분을 디버깅 할 땐 편하다.</p>
<p>근데 flaky test 디버깅은 진짜 지옥이다.</p>
<p>테스트 프레임워크를 기본적으로 쓰는데, 이 설정을 디버깅하는 건 정말 어렵다.</p>
<h2 id="테스트하기-좋은-구조로-바꾸는게-정말-코드-품질에-도움이-될까">테스트하기 좋은 구조로 바꾸는게 정말 코드 품질에 도움이 될까?</h2>
<p>flaky test를 겪고 나면 테스트하기 좋은 구조로 구현체를 짜기 시작한다.</p>
<p>대체로 테스트하기 좋은 구조는 객체지향적으로 응집성이 좋아질 수 있다.</p>
<p>근데 언어 한계상, 테스트가 불가능한 구간도 존재하고
테스트가 잘 되게 분리하려고 다른 구현 방법을 쓰는 게 코드 품질에는 더 안 좋은 선택이 될 수 있다.</p>
<p>언제나 상황에 따라서 최적의 트레이드오프를 선택하는 게 먼저지
테스트를 위해서 트레이드오프를 바꾸는 건 주객전도다.</p>
<h2 id="테스트-성공이라고-진짜-성공한건가">테스트 성공이라고 진짜 성공한건가?</h2>
<p>애초에 내가 설계를 잘못 했다면 테스트가 아무리 성공해도 내 의도와 맞지 않고 치명적인 에러가 쌓일 수 있다.</p>
<h1 id="tdd에-대한-오해">TDD에 대한 오해</h1>
<p>사람들이 TDD 장점이라고 써놓는 거 대부분 위에서 다 깠다. 리팩토링, 코드 품질 이런 거. 그 얘기는 넘어가겠다.</p>
<h2 id="검증서를-먼저-만듬">검증서를 먼저 만듬</h2>
<p>내가 시리즈에서 다뤘듯이 테스트는 계약(인터페이스)에 의존한다.</p>
<p>테스트는 구현체랑 상관이 없다. 그래서 구현체보다 먼저 쓰는 게 가능하다.</p>
<p>이게 TDD의 끝이다.</p>
<p>계약 검증서부터 먼저 만든다.</p>
<h2 id="red-green-refactor의-진짜-의미">Red-Green-Refactor의 진짜 의미</h2>
<p>Red-Green-Refactor는 방법론이 아니다. 그냥 부산물이다.</p>
<p>검증 문서인 테스트를 구현체보다 먼저 작성한다는 걸 생각해보면, 이 순서는 그냥 당연히 나온다.</p>
<p>Red(실패)
계약에 대한 검증서를 먼저 작성한다.
-&gt; 검증서는 있는데 구현체가 없으니까 당연히 실패한다.</p>
<p>Green(성공)
테스트를 통과할 수 있는 최소한의 코드를 작성한다.
-&gt; 구현체가 계약을 만족했으면 테스트가 성공한다.</p>
<p>Refactor(리팩토링)
테스트가 통과하면 코드를 더 깔끔하게 개선한다.
-&gt;테스트가 있으니 리팩토링하면서도 계약을 계속 확인할 수 있다.
성공하면 계약 만족, 실패하면 다시 리팩토링 해야한다.</p>
<p>결론은 이거다.</p>
<p>그냥 계약 검증서가 있어서 구현체 만들 때마다 검증하는 거지,
이 절차를 의식적으로 따라야 할 이유는 없다.</p>
<p>그냥 계약을 잘 작성하고 그걸 검증하는 테스트를 만들어라.</p>
<h2 id="red-green-refactor을-맹신하면-안-되는-이유">Red-Green-Refactor을 맹신하면 안 되는 이유</h2>
<p>Red
일단 실패하는 테스트를 쓰라고 하니까, 계약 검증이라는 목적은 잊고 그냥 대충 테스트를 만든다.</p>
<p>Green
계약이 아니라 구현체를 맞춰서 테스트를 통과시키고, 그걸 잘했다고 착각한다.</p>
<p>Refactor
나중에 리팩토링 한다고 안심한다.
근데 나중에 바빠서 리팩토링 안 한다.</p>
<h2 id="tdd를-쓰는-상황과-장점">TDD를 쓰는 상황과 장점</h2>
<p>TDD는 익스트림한 상황에서 쓴다. 보통 크게 2가지다.</p>
<ol>
<li>인력, 기간, 자원이 한정돼 있는데 개발은 해줘야 한다.</li>
<li>시스템이 계속 진화한다.</li>
</ol>
<p>2번은 그냥 필연이다.
굳이 테스트를 먼저 작성해야만 해결되는 문제가 아니다.</p>
<p>1번에서 TDD의 장점은 하나다.</p>
<p>정해진 기간과 인력, 자원이 있는 경우에 클라이언트와 공급사는 계약을 맺는다.
정해진 일시까지 어떤 요구사항을 만족시키라고 계약을 맺는다.</p>
<p>바쁜 상황에서는 품질이고 뭐고 일단 계약사항을 만족시켜야 한다.</p>
<p>그러니까 소프트웨어가 계약을 만족했는지 검사하는 검증서부터 만들고,
검증서를 통과하면 계약은 통과했으니 빨리 다음 기능으로 넘어간다.</p>
<p>이게 TDD가 먹히는 이유다.</p>
<h2 id="단점">단점</h2>
<p>당연히 계약을 빨리 만족하고 넘어가는 방식이라 코드 품질 보장은 못 한다.</p>
<p>Red-Green-Refactor 같은 과정을 들이밀면서 코드 품질에 면죄부 주는 분위기가 되기 쉬워서,
바쁜 상황에서만 고려할만 하다.</p>
<h2 id="나는-tdd를-쓰나">나는 TDD를 쓰나?</h2>
<p>지금 나는 바쁜 상황이 아니다. 프레임워크 만드는 상황이다.</p>
<p>당연히 코드 품질, 설계, 객체지향, 리스크 관리에 더 힘써야 한다.</p>
<p>굳이 테스트를 먼저 작성할 필요가 없어서 테스트를 먼저 쓰지 않는다.</p>
<p>기간이 촉박한 상황에서 최소한 계약은 지켰다는 걸 보장해야 되는 상황이면 그땐 쓸 듯하다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TDD를 부숴주마 - 어떻게 테스트 하는가?]]></title>
            <link>https://velog.io/@sour_grape/TDD%EB%A5%BC-%EB%B6%80%EC%88%B4%EC%A3%BC%EB%A7%88-%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%85%8C%EC%8A%A4%ED%8A%B8-%ED%95%98%EB%8A%94%EA%B0%80</link>
            <guid>https://velog.io/@sour_grape/TDD%EB%A5%BC-%EB%B6%80%EC%88%B4%EC%A3%BC%EB%A7%88-%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%85%8C%EC%8A%A4%ED%8A%B8-%ED%95%98%EB%8A%94%EA%B0%80</guid>
            <pubDate>Fri, 13 Feb 2026 07:39:58 GMT</pubDate>
            <description><![CDATA[<h1 id="테스트는-구현체와-관계가-없다">테스트는 구현체와 관계가 없다</h1>
<p>테스트는 “구현체”가 아니라 <strong>계약(인터페이스)</strong> 을 검증한다.<br>그래서 계약만 잘 정의되어 있으면 구현체가 아직 없어도 테스트를 먼저 만들 수 있다.</p>
<p>관계는 이렇다.</p>
<ul>
<li>인터페이스(계약) --- 테스트(검증서)</li>
<li>인터페이스(계약) --- 구현체(실제 동작)</li>
</ul>
<p>즉, 테스트는 구현체가 아니라 <strong>인터페이스에 의존</strong>한다.</p>
<hr>
<h2 id="tdd">TDD</h2>
<p>테스트가 인터페이스에 의존하니까, 구현체보다 테스트를 먼저 만들어도 된다.<br>이게 TDD의 모든 것이다</p>
<hr>
<h1 id="계약-테스트는-무엇을-만족해야-하나">계약 테스트는 무엇을 만족해야 하나?</h1>
<p>계약 테스트는 조건이 두 개다.</p>
<ol>
<li>계약을 검증해야 한다.</li>
<li>구현체에 의존하면 안 된다.</li>
</ol>
<p>2번을 만족시키려면 어떻게 하냐?</p>
<p>우리가 평소에 구체 클래스 의존을 끊을 때 쓰는 방법 그대로 쓰면 된다.</p>
<ul>
<li><strong>추상화(인터페이스/추상 클래스)</strong></li>
<li><strong>DI(구현체를 밖에서 주입)</strong></li>
</ul>
<p>핵심은 이거다.</p>
<blockquote>
<p>계약 테스트 템플릿은 구현체를 <code>new</code> 하지 않는다.<br>구현체는 밖에서 <strong>주입(DI)</strong> 한다.</p>
</blockquote>
<hr>
<h1 id="예제-checkout주문-결제">예제: Checkout(주문 결제)</h1>
<h2 id="계약비즈니스-규칙">계약(비즈니스 규칙)</h2>
<ul>
<li><code>pay(wallet, price)</code>는 결제를 시도한다.</li>
<li>잔액이 충분하면 결제는 <strong>성공(true)</strong> 하고, 잔액이 <strong>price만큼 감소</strong>한다.</li>
<li>잔액이 부족하면 결제는 <strong>실패(false)</strong> 하고, 잔액은 <strong>변하지 않는다</strong>.</li>
</ul>
<h2 id="검증-목록">검증 목록</h2>
<ul>
<li><strong>검증 1)</strong> 잔액이 충분하면 <code>pay == true</code> 이고, <code>잔액 == 기존잔액 - price</code></li>
<li><strong>검증 2)</strong> 잔액이 부족하면 <code>pay == false</code> 이고, <code>잔액 == 기존잔액</code></li>
<li><strong>검증 3)</strong> <code>price &lt;= 0</code> 이면 예외</li>
<li><strong>검증 4)</strong> 연속 결제에서도 규칙 유지 (성공 -&gt; 감소, 실패 -&gt; 불변)</li>
</ul>
<h2 id="계약인터페이스">계약(인터페이스)</h2>
<pre><code class="language-java">public interface Checkout {
    boolean pay(Wallet wallet, int price);
}

public interface Wallet {
    int balance();
    void withdraw(int amount);
}</code></pre>
<blockquote>
<p>NOTE: Wallet을 실제 구현체/스텁/페이크/목 중 무엇으로 대체할지는 중요하지만<br>이 글의 주제는 “인터페이스 vs 추상 클래스 + DI 구조”라서, 더블 전략은 다음 파트로 넘긴다.</p>
</blockquote>
<hr>
<h1 id="계약-테스트를-재사용하는-2가지-방법">계약 테스트를 재사용하는 2가지 방법</h1>
<p>둘 다 목표는 동일하다.</p>
<blockquote>
<p>계약 검증 로직은 <strong>한 번만</strong> 작성하고,<br>구현체가 바뀌면 <strong>주입만</strong> 바꿔 끼운다(DI).</p>
</blockquote>
<p>차이는 “테스트 템플릿을 무엇으로 만들까”다.</p>
<hr>
<h2 id="1-추상-클래스-기반-픽스처셋업이-쉬움">1) 추상 클래스 기반 (픽스처/셋업이 쉬움)</h2>
<h3 id="왜-쓰나">왜 쓰나?</h3>
<ul>
<li><strong>추상 클래스는 값을 가질 수 있다.</strong><br>그래서 공통 픽스처(예: <code>price</code>)나 공통 셋업을 <code>protected final</code> 필드로 들고 갈 수 있다.</li>
<li>계약 테스트(검증 1~4)는 템플릿에 <strong>한 번만</strong> 쓴다.</li>
<li>구현체별 테스트 클래스는 <code>createCheckout()</code>만 오버라이드해서 구현체를 <strong>주입(DI)</strong> 한다.</li>
</ul>
<pre><code class="language-java">import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

public abstract class CheckoutContractTest_Abstract {

    // DI 포인트: 구현체 주입
    protected abstract Checkout createCheckout();

    // 공통 값(픽스처)을 필드로 보유 가능
    protected final int price = 500;

    // Wallet 준비도 DI로 밀어둔다 (대체 전략은 뒤 파트에서)
    protected abstract Wallet createWallet(int initialBalance);

    @Test
    @DisplayName(&quot;검증 1) 잔액이 충분하면 pay == true 이고 잔액 == 기존잔액 - price&quot;)
    void succeeds_and_decreases_balance_when_enough_funds() {
        Checkout sut = createCheckout();
        Wallet wallet = createWallet(2000);

        boolean ok = sut.pay(wallet, price);

        assertTrue(ok);
        assertEquals(1500, wallet.balance());
    }

    @Test
    @DisplayName(&quot;검증 2) 잔액이 부족하면 pay == false 이고 잔액 == 기존잔액&quot;)
    void fails_and_does_not_change_balance_when_insufficient_funds() {
        Checkout sut = createCheckout();
        Wallet wallet = createWallet(300);
        int before = wallet.balance();

        boolean ok = sut.pay(wallet, price);

        assertFalse(ok);
        assertEquals(before, wallet.balance());
    }

    @Test
    @DisplayName(&quot;검증 3) price &lt;= 0 이면 예외&quot;)
    void throws_when_price_is_zero_or_negative() {
        Checkout sut = createCheckout();
        Wallet wallet = createWallet(1000);

        assertThrows(IllegalArgumentException.class, () -&gt; sut.pay(wallet, 0));
        assertThrows(IllegalArgumentException.class, () -&gt; sut.pay(wallet, -1));
    }

    @Test
    @DisplayName(&quot;검증 4) 연속 결제에서도 규칙이 유지된다 (성공 -&gt; 잔액 감소, 실패 -&gt; 잔액 불변)&quot;)
    void remains_consistent_across_repeated_payments() {
        Checkout sut = createCheckout();
        Wallet wallet = createWallet(1000);

        assertTrue(sut.pay(wallet, 400));
        assertEquals(600, wallet.balance());

        assertTrue(sut.pay(wallet, 400));
        assertEquals(200, wallet.balance());

        int before = wallet.balance();
        assertFalse(sut.pay(wallet, 400));
        assertEquals(before, wallet.balance());
    }
}</code></pre>
<p>구현체별 테스트 클래스는 “주입만” 담당한다.</p>
<pre><code class="language-java">public class SimpleCheckoutContractTest_Abstract extends CheckoutContractTest_Abstract {

    @Override
    protected Checkout createCheckout() {
        return new SimpleCheckout(); // 구현체 주입
    }

    @Override
    protected Wallet createWallet(int initialBalance) {
        return new DemoWallet(initialBalance); // (대체 전략은 뒤 파트에서)
    }
}</code></pre>
<hr>
<h2 id="2-인터페이스-기반-다중-계약-조합이-쉬움">2) 인터페이스 기반 (다중 계약 조합이 쉬움)</h2>
<h3 id="왜-쓰나-1">왜 쓰나?</h3>
<ul>
<li><strong>추상 클래스는 다중 상속이 불가능</strong>하다.</li>
<li>구현체가 계약(인터페이스)을 여러 개 구현하면, 계약 테스트도 여러 개를 “조립”해서 붙이고 싶다.</li>
<li>테스트 템플릿을 인터페이스로 만들면, 테스트 클래스가 여러 계약 테스트를 동시에 구현할 수 있다.</li>
</ul>
<pre><code class="language-java">import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

public interface CheckoutContractTest_Interface {

    // DI 포인트: 구현체 주입
    Checkout createCheckout();

    // Wallet 준비도 DI로 밀어둔다 (대체 전략은 뒤 파트에서)
    Wallet createWallet(int initialBalance);

    @Test
    @DisplayName(&quot;검증 1) 잔액이 충분하면 pay == true 이고 잔액 == 기존잔액 - price&quot;)
    default void succeeds_and_decreases_balance_when_enough_funds() {
        Checkout sut = createCheckout();
        Wallet wallet = createWallet(2000);
        int price = 500; // 인터페이스는 값(상태) 보유가 어려워 로컬로 둔다

        boolean ok = sut.pay(wallet, price);

        assertTrue(ok);
        assertEquals(1500, wallet.balance());
    }

    @Test
    @DisplayName(&quot;검증 2) 잔액이 부족하면 pay == false 이고 잔액 == 기존잔액&quot;)
    default void fails_and_does_not_change_balance_when_insufficient_funds() {
        Checkout sut = createCheckout();
        Wallet wallet = createWallet(300);
        int price = 500;
        int before = wallet.balance();

        boolean ok = sut.pay(wallet, price);

        assertFalse(ok);
        assertEquals(before, wallet.balance());
    }

    @Test
    @DisplayName(&quot;검증 3) price &lt;= 0 이면 예외&quot;)
    default void throws_when_price_is_zero_or_negative() {
        Checkout sut = createCheckout();
        Wallet wallet = createWallet(1000);

        assertThrows(IllegalArgumentException.class, () -&gt; sut.pay(wallet, 0));
        assertThrows(IllegalArgumentException.class, () -&gt; sut.pay(wallet, -1));
    }

    @Test
    @DisplayName(&quot;검증 4) 연속 결제에서도 규칙이 유지된다 (성공 -&gt; 잔액 감소, 실패 -&gt; 잔액 불변)&quot;)
    default void remains_consistent_across_repeated_payments() {
        Checkout sut = createCheckout();
        Wallet wallet = createWallet(1000);

        assertTrue(sut.pay(wallet, 400));
        assertEquals(600, wallet.balance());

        assertTrue(sut.pay(wallet, 400));
        assertEquals(200, wallet.balance());

        int before = wallet.balance();
        assertFalse(sut.pay(wallet, 400));
        assertEquals(before, wallet.balance());
    }
}</code></pre>
<p>구현체별 테스트 클래스는 역시 “주입만” 담당한다.</p>
<pre><code class="language-java">public class SimpleCheckoutContractTest_Interface implements CheckoutContractTest_Interface {

    @Override
    public Checkout createCheckout() {
        return new SimpleCheckout(); // 구현체 주입
    }

    @Override
    public Wallet createWallet(int initialBalance) {
        return new DemoWallet(initialBalance); // (대체 전략은 뒤 파트에서)
    }
}</code></pre>
<hr>
<h1 id="트레이드-오프">트레이드 오프</h1>
<h2 id="추상-클래스">추상 클래스</h2>
<ul>
<li>장점: <strong>값(상태)을 가질 수 있어서</strong> 공통 픽스처/셋업이 편하다.</li>
<li>단점: 단일 상속이라 계약 테스트 템플릿을 여러 개 조합하기 어렵다.</li>
</ul>
<h2 id="인터페이스">인터페이스</h2>
<ul>
<li>장점: 테스트 템플릿을 여러 개 구현해서 <strong>조립</strong>할 수 있다.</li>
<li>단점: <strong>값(상태)을 들고 가기 어렵다</strong> 보니 공통 픽스처가 로컬/헬퍼로 흩어지기 쉽다.</li>
</ul>
<hr>
<h2 id="선택-가이드">선택 가이드</h2>
<ul>
<li>셋업/픽스처가 길고 공통이 많으면 → <strong>추상 클래스</strong></li>
<li>구현체가 계약(인터페이스)을 여러 개 구현하고, 테스트도 조립이 필요하면 → <strong>인터페이스</strong></li>
<li>둘 다 필요하면 → “검증 로직”을 헬퍼로 분리하고 템플릿은 얇게 유지</li>
</ul>
<p>나는 평소에 추상 클래스를 선호한다.
테스트에서 setup 과정이 대부분의 시간과 노력을 잡아먹는데 인터페이스 테스트는 너무 귀찮아서 여러개 조립 하는거 아니면 추상클래스로 테스트를 만든다.</p>
<hr>
<h1 id="검증서보증서로서의-테스트란">검증서(보증서)로서의 테스트란?</h1>
<p>테스트는 검증서다
정확히는 아래를 뜻한다.</p>
<blockquote>
<p>구현체가 어떻게 바뀌든 간에, <strong>구현체가 계약(인터페이스)을 이행하는지</strong>를  
반복적으로 확인해 주는 문서(자동 검증 장치)다.</p>
</blockquote>
<p>즉 테스트가 보증하는 건 “코드가 돌아간다”가 아니라 <strong>불변조건(invariant)</strong> 이다.</p>
<ul>
<li>바뀌어도 괜찮다: 구현 방식(알고리즘/구조/최적화/리팩토링/캐싱 추가)</li>
<li>이것만은 바뀌면 안 된다: <strong>계약(규칙)</strong><br>예: “충분하면 성공+차감 / 부족하면 실패+불변 / price&lt;=0이면 예외”</li>
</ul>
<hr>
<h2 id="1-리팩토링-구현을-갈아엎어도-계약을-만족하는지-확인">1) 리팩토링: 구현을 갈아엎어도 “계약”을 만족하는지 확인</h2>
<p>리팩토링은 내부를 바꾸는 작업이다.<br>그런데 내부 변경은 쉽게 계약을 깨뜨린다.</p>
<ul>
<li>최적화하다가 부족한데도 차감해버림</li>
<li>예외 처리 리팩토링하다가 <code>price&lt;=0</code> 검증이 빠짐</li>
<li>캐시를 넣다가 연속 결제에서 상태가 꼬임</li>
</ul>
<p>이때 테스트는 “리팩토링이 예쁘다/빠르다”를 평가하지 않는다.<br>오직 하나만 본다.</p>
<blockquote>
<p><strong>리팩토링 전과 후가 같은 계약을 만족하는가?</strong></p>
</blockquote>
<p>그래서 테스트는 리팩토링의 브레이크/가드레일 역할을 한다.</p>
<hr>
<h2 id="2-하나의-계약에-구현체가-여러-개-모든-구현체가-같은-규칙을-따르는지-확인">2) 하나의 계약에 구현체가 여러 개: “모든 구현체가 같은 규칙을 따르는지” 확인</h2>
<p><code>Checkout</code> 구현체는 상황에 따라 여러 개가 될 수 있다.</p>
<ul>
<li><code>SimpleCheckout</code> (메모리 기반)</li>
<li><code>DbCheckout</code> (DB 트랜잭션)</li>
<li><code>RemoteCheckout</code> (외부 결제 API)</li>
</ul>
<p>구현 방식은 다르지만, 사용자 입장에서는 계약이 하나다.</p>
<blockquote>
<p><code>Checkout</code>라는 이름으로 제공되는 기능은 어디서든 같은 의미를 가져야 한다.</p>
</blockquote>
<p>그래서 계약 테스트를 한 번 작성해두면, 구현체가 늘어날 때마다<br>그 구현체에 계약 테스트를 “붙이기만” 하면 된다.</p>
<ul>
<li>테스트를 복사하지 않는다.</li>
<li>규칙이 바뀌면 테스트 한 군데만 고치면 된다.</li>
<li>구현체가 늘어도 “계약이 동일하다”는 사실이 자동으로 유지된다.</li>
</ul>
<hr>
<h2 id="3-하나의-구현체가-계약을-여러-개-구현-여러-보증서로-한-구현체를-검증한다">3) 하나의 구현체가 계약을 여러 개 구현: “여러 보증서로 한 구현체를 검증”한다</h2>
<p>어떤 구현체는 계약을 여러 개 만족해야 한다.</p>
<p>예: <code>PaymentService implements Checkout, Refundable, Auditable</code></p>
<p>이 구현체는 “결제 규칙”만 지키면 끝이 아니다.</p>
<ul>
<li>Checkout 계약을 지켜야 하고</li>
<li>Refundable 계약도 지켜야 하고</li>
<li>Auditable 계약도 지켜야 한다</li>
</ul>
<p>이때 중요한 건 “구현체 단위로 테스트를 쓰는 것”이 아니라,<br><strong>계약 단위로 쪼개진 검증서</strong>를 조립해서 붙이는 것이다.</p>
<blockquote>
<p>구현체는 바뀌어도, 계약별 검증서는 독립적으로 존재하고,<br>구현체는 그 검증서들을 모두 통과해야 한다.</p>
</blockquote>
<pre><code class="language-java">// 계약(인터페이스) 여러 개
public interface Checkout {
    boolean pay(Wallet wallet, int price);
}

public interface Refundable {
    boolean refund(Wallet wallet, int amount);
}

public interface Auditable {
    void audit(String message);
}

// 구현체가 인터페이스(계약) 여러 개를 동시에 구현하는 구조
public class PaymentService implements Checkout, Refundable, Auditable {
    @Override public boolean pay(Wallet wallet, int price) { return true; }
    @Override public boolean refund(Wallet wallet, int amount) { return true; }
    @Override public void audit(String message) {}
}</code></pre>
<p>그래서 계약 테스트를 인터페이스 기반으로 만들면(조합 가능),<br>한 구현체에 여러 계약 테스트를 동시에 붙여 검증할 수 있다.</p>
<hr>
<h2 id="주절-주절-말이-많았는데">주절 주절 말이 많았는데</h2>
<p>검증서인 테스트를 만들어 놓으면 심판자 역할을 한다.</p>
<p><strong>내가 최적화를 하고 분리하고 코드 품질을 위해 온갖 노력을 다 해도 계약 테스트를 통과 못하면 헛수고란걸 바로바로 알 수 있다.</strong></p>
<p>그래서 다시 고쳐야되는걸 바로 알려준다.</p>
<hr>
<h1 id="결론">결론</h1>
<p>테스트가 “검증서”라는 말의 핵심은 이것이다.</p>
<ul>
<li>테스트는 구현체를 설명하지 않는다.</li>
<li>테스트는 구현체가 계약을 <strong>이행하는지</strong>를 보증한다.</li>
<li>구현은 자유롭게 바꿔도 되지만(리팩토링/최적화/구현체 추가),
계약을 깨는 순간 테스트가 즉시 알려준다.</li>
</ul>
<p>그래서 어떤 경우든, 테스트는 구현체가 아니라 <strong>계약의 보증서</strong>로 남아야 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TDD를 부숴주마 - 무엇을 테스트 하는가?]]></title>
            <link>https://velog.io/@sour_grape/TDD%EB%A5%BC-%EB%B6%80%EC%88%B4%EC%A3%BC%EB%A7%88-%EB%AC%B4%EC%97%87%EC%9D%84-%ED%85%8C%EC%8A%A4%ED%8A%B8-%ED%95%98%EB%8A%94%EA%B0%80</link>
            <guid>https://velog.io/@sour_grape/TDD%EB%A5%BC-%EB%B6%80%EC%88%B4%EC%A3%BC%EB%A7%88-%EB%AC%B4%EC%97%87%EC%9D%84-%ED%85%8C%EC%8A%A4%ED%8A%B8-%ED%95%98%EB%8A%94%EA%B0%80</guid>
            <pubDate>Thu, 12 Feb 2026 09:49:47 GMT</pubDate>
            <description><![CDATA[<h1 id="테스트는-왜-어려운가">테스트는 왜 어려운가?</h1>
<p>무엇을 테스트 할 지 모르기 때문이다.</p>
<p>무엇을 테스트 할 지 모르기 때문에 TDD가 유행했다고 생각한다.</p>
<p>TDD는 레드-그린-리팩토링과 같은 잘못된 방법을 설파한다.</p>
<p><strong>Red (실패): 구현하고자 하는 기능의 테스트 코드를 먼저 작성한다. 테스트는 실패해야 한다.</strong></p>
<p><strong>Green (통과): 실패한 테스트를 통과할 만큼의 최소한의 실제 코드를 작성한다.</strong></p>
<p><strong>Refactor (리팩토링): 구현된 코드를 정리하고 최적화한다. 테스트는 통과 상태를 유지해야 한다.</strong></p>
<p><strong>이 방법은 완벽하게 개소리이고 이 방법론 때문에 개발자에게 잘못된 면죄부를 주게 된다.</strong></p>
<ol>
<li><p>무엇을 테스트 할 지 이해하지 못한 상태에서 실패하는 테스트를 먼저 작성하라는 건 무모한 용기를 주게된다.</p>
</li>
<li><p>통과하는 테스트로 개선하므로 옳게 테스트 했다는 자만심을 심어주고</p>
</li>
<li><p>리팩터링 할 때 테스트가 통과했으니 코드 품질이 높아졌다는 착각을 준다.</p>
</li>
</ol>
<p>무엇을 테스트 할 지 모르는 상태에서 테스트 본질 자체를 이해하지 못하고 결국엔 BDD 형식, TDD, 코드 품질등의 단어에 집착하는 광신도가 된다.</p>
<h1 id="무엇을-테스트-하는가">무엇을 테스트 하는가?</h1>
<p>앞선 글에서 계약에 대해 살펴보고 테스트는 계약의 검증서란걸 말했다.
다시 한 번 말하겠다.</p>
<p><strong>테스트는 계약을 검증하는 살아있는 검증문서이다.</strong></p>
<p>당연히 테스트는 계약의 검증문서이므로 <strong>계약을 테스트 해야한다.</strong></p>
<hr>
<h2 id="예제">예제</h2>
<p>계약은 어떤게 있고 어떻게 작성하는지는 뒷 시리즈에서 설명하겠다.
우선 간단한 계약사항을 가져왔다</p>
<h3 id="계약-주문-총액-계산기-ordertotalcalculator">계약: 주문 총액 계산기 (OrderTotalCalculator)</h3>
<p><strong>계약</strong></p>
<ol>
<li>총액은 모든 라인아이템의 <strong>단가 * 수량</strong>을 <strong>합한 값</strong>이어야 한다.</li>
<li><strong>단가 &lt; 0 금지</strong>, <strong>수량 &lt;= 0 금지</strong>. 위반 시 <strong>반드시 예외</strong>를 던져야 한다.</li>
<li>계산은 <strong>반드시 정수</strong>로 한다. (원 단위)</li>
</ol>
<hr>
<p>계약이 잘 정의되고 정제되어 있으면 당연히 무엇을 검증할지 너무도 명백하다</p>
<h2 id="검증-계약을-검증하는-테스트-목록">검증 (계약을 검증하는 테스트 목록)</h2>
<ul>
<li>검증 1) total == Σ(단가 * 수량)</li>
<li>검증 2) 단가 &lt; 0 이면 예외</li>
<li>검증 3) 수량 &lt;= 0 이면 예외</li>
<li>검증 4) 정수가 아닌 계산이면 예외</li>
</ul>
<hr>
<h3 id="검증-1-total--σ단가--수량">검증 1) total == Σ(단가 * 수량)</h3>
<pre><code class="language-java">@Test
@DisplayName(&quot;검증 1) total == Σ(단가 * 수량)&quot;)
void total_equals_sum_of_unitPrice_times_quantity() {

    // (계약 구현체 준비)
    계약구현체 구현체1 = new 계약구현체();

    // (계약값 준비) 정상 라인아이템들
    라인아이템들 계약값 = 라인아이템들.of(
        라인아이템.of(단가=1000, 수량=2),   // 2000
        라인아이템.of(단가=500,  수량=3)    // 1500
    );

    // (검증) total == 3500
    assertEquals(3500, 구현체1.total(계약값));
}</code></pre>
<hr>
<h3 id="검증-2-단가--0-이면-예외">검증 2) 단가 &lt; 0 이면 예외</h3>
<pre><code class="language-java">@Test
@DisplayName(&quot;검증 2) 단가 &lt; 0 이면 예외&quot;)
void throws_when_unitPrice_is_negative() {

    // (계약 구현체 준비)
    계약구현체 구현체1 = new 계약구현체();

    // (계약값 준비) 단가가 음수인 라인아이템 포함
    라인아이템들 계약값 = 라인아이템들.of(
        라인아이템.of(단가=-1, 수량=1)
    );

    // (검증) 계약 위반 -&gt; 반드시 예외
    assertThrows(예외.class, () -&gt; 구현체1.total(계약값));
}</code></pre>
<hr>
<h3 id="검증-3-수량--0-이면-예외">검증 3) 수량 &lt;= 0 이면 예외</h3>
<pre><code class="language-java">@Test
@DisplayName(&quot;검증 3) 수량 &lt;= 0 이면 예외&quot;)
void throws_when_quantity_is_zero_or_negative() {

    // (계약 구현체 준비)
    계약구현체 구현체1 = new 계약구현체();

    // (계약값 준비) 수량 0
    라인아이템들 계약값1 = 라인아이템들.of(
        라인아이템.of(단가=1000, 수량=0)
    );

    // (검증) 계약 위반 -&gt; 반드시 예외
    assertThrows(예외.class, () -&gt; 구현체1.total(계약값1));

    // (계약값 준비) 수량 음수
    라인아이템들 계약값2 = 라인아이템들.of(
        라인아이템.of(단가=1000, 수량=-1)
    );

    // (검증) 계약 위반 -&gt; 반드시 예외
    assertThrows(예외.class, () -&gt; 구현체1.total(계약값2));
}</code></pre>
<hr>
<h3 id="검증-4-정수가-아닌-계산이면-예외">검증 4) 정수가 아닌 계산이면 예외</h3>
<pre><code class="language-java">@Test
@DisplayName(&quot;검증 4) 정수가 아닌 계산이면 예외&quot;)
void throws_when_non_integer_calculation_is_attempted() {

    // (계약 구현체 준비)
    계약구현체 구현체1 = new 계약구현체();

    // (계약값 준비) 소수 단가(원 단위 위반)
    라인아이템들 계약값 = 라인아이템들.of(
        라인아이템.of(단가=10.5, 수량=2)
    );

    // (검증) 정수 계약 위반 -&gt; 반드시 예외
    assertThrows(예외.class, () -&gt; 구현체1.total(계약값));
}</code></pre>
<hr>
<h1 id="테스트-형식">테스트 형식</h1>
<p>위의 예제에서 테스트 예제를 적었는데 3가지 포인트가 있다</p>
<ol>
<li><p>Displayname는 검증사항을 적는다. -&gt; 테스트가 검증 문서이므로 명시적으로 검증사항을 적어야 한다.</p>
</li>
<li><p>assert문이 가장 중요하다.</p>
</li>
<li><p>그 위에는 setup이고 구현 부산물이라 테크닉적인 영역이다. 의미는 거의 없다</p>
</li>
</ol>
<p>테스트를 만든 개발자들이 assert라고 대놓고 박아놨다.</p>
<p>assert의 뜻은 단언하다라는 뜻이다. 무엇을 단언하는 걸까?</p>
<p><strong>구현체는 /단언하다 /계약사항을 만족함을</strong></p>
<p>assert문이 있다는 것 자체가 테스트는 검증문서 역할을 해야한다는 증거이다.</p>
<p>테스트에서 assert문이 가장 중요하고 나머지 위 코드는 setup의 과정이다.</p>
<p>검증문을 작성하기 위해선 계약값, 구현체 값 2개가 필요하다. setup과정은 이 2가지를 만드는데 의미가 있다.</p>
<h1 id="테스트를-쉽게-하려면">테스트를 쉽게 하려면</h1>
<p>테스트가 어려운 이유는 무엇을 테스트 할지 막막해서이다.
우리가 계약 사항을 명시적으로 설계하고 작성한다면 무엇을 검증할 지 너무도 명확해진다.</p>
<p>무엇을 검증할 지 명확해지면 assert문을 작성하기 너무 쉬워지고 setup의 과정은 귀찮은 영역일 뿐 테스트를 쉽게 작성할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TDD를 부숴주마 - 이 모든것은 인터페이스 때문이다]]></title>
            <link>https://velog.io/@sour_grape/TDD%EB%A5%BC-%EB%B6%80%EC%88%B4%EC%A3%BC%EB%A7%88-%EC%9D%B4-%EB%AA%A8%EB%93%A0%EA%B2%83%EC%9D%80-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%EB%95%8C%EB%AC%B8%EC%9D%B4%EB%8B%A4</link>
            <guid>https://velog.io/@sour_grape/TDD%EB%A5%BC-%EB%B6%80%EC%88%B4%EC%A3%BC%EB%A7%88-%EC%9D%B4-%EB%AA%A8%EB%93%A0%EA%B2%83%EC%9D%80-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%EB%95%8C%EB%AC%B8%EC%9D%B4%EB%8B%A4</guid>
            <pubDate>Wed, 11 Feb 2026 07:48:30 GMT</pubDate>
            <description><![CDATA[<h1 id="시리즈의-결론">시리즈의 결론</h1>
<p>원래는 차근차근 빌드업 쌓고 결론을 뒤에 박으려고 했다.<br>근데 그냥 시작할 때 박아두고, 시리즈 내내 반복해서 때리기로 했다.</p>
<p><strong>테스트는 계약을 검증하는 살아있는 검증문서이다.</strong></p>
<p>이 한 줄만 제대로 이해하면, TDD가 왜 그렇게까지 신성시될 필요가 없는지 바로 보인다.<br>테스트 작성의 목적은 딱 이거다. <strong>계약을 검증하는 것.</strong><br>나머지는 그 과정에서 생기는 <strong>구현 부산물</strong>일 뿐이다.</p>
<p>이걸 이해했다면, 솔직히 이 시리즈 안 읽어도 된다.<br>뒤에 내가 테스트를 어떻게 짜는지 보여주긴 할 건데, 그건 어디까지나 “살아있는 검증문서” 만들다 보면 자연스럽게 따라오는 부산물이다.<br>핵심은 방법론이 아니라 <strong>계약</strong>이다.</p>
<hr>
<h1 id="이-모든-건-인터페이스-때문이다">이 모든 건 인터페이스 때문이다</h1>
<p>사실 이 내용만 따로 시리즈로 빼도 될 만큼 중요하다.<br>근데 너무 중요해서 어디서부터 손대야 할지도 감이 안 온다.<br>일단은 여기서 핵심만 박아두고, 나중에 제대로 파겠다.</p>
<hr>
<h2 id="인터페이스의-본질">인터페이스의 본질</h2>
<p>한국에서 인터페이스를 학습하거나 검색해보면 보통 이런 말부터 나온다.</p>
<ul>
<li>객체지향</li>
<li>디자인 패턴</li>
<li>추상화</li>
<li>다형성</li>
<li>상속의 대체</li>
</ul>
<p>정의 외우고, 용어 외우고, 도대체 뭔 짓거리인지 모르겠다.</p>
<p><strong>인터페이스의 본질은 계약이다.</strong></p>
<p>인터페이스는 <strong>계약을 소프트웨어에서 표현하고 강제하는 수단</strong>이다.<br>인터페이스는 그냥 계약이다. 이게 본질이다.</p>
<p>물론 한국의 모든 사람이 이걸 모른다는 말은 아니다.<br>근데 내가 학습하고, 대화하고, 검색하고, 면접 보고, 개발하면서 본 경험 기준으로는<br>“계약”이라는 말을 <strong>정의로 박아놓고 시작하는 경우를 거의 못 봤다.</strong><br>있어도 대개 “비유”로 스쳐 지나가거나, 뒤쪽에 살짝 붙는다.</p>
<p>그래서 인터페이스를 계약으로 안 보게 되고,<br>그걸 검증하는 테스트도 계약 검증으로 안 보게 되고,<br>거기서부터 모든 오해가 시작된다.</p>
<p>나도 실제로 기술면접에서 “인터페이스가 뭐에요?” 같은 질문을 받았고<br>그냥 “인터페이스는 계약입니다”라고 답했더니<br>“인터페이스를 전혀 모르네요” 소리를 들었다.</p>
<p>내가 이걸 뇌피셜로 떠드는 거라고 생각하는 사람도 많을 거다.<br>그래서 공식 문서를 그냥 박아둔다.</p>
<h3 id="what-is-an-interface-oracle-java-tutorials">What Is an Interface? (Oracle Java Tutorials)</h3>
<p><a href="https://docs.oracle.com/javase/tutorial/java/concepts/interface.html">https://docs.oracle.com/javase/tutorial/java/concepts/interface.html</a></p>
<p>“Interfaces form a contract between the class and the outside world.”<br>“this contract is enforced at build time by the compiler.”</p>
<h3 id="microsoft-learn--c-language-reference">Microsoft Learn — C# language reference</h3>
<p><a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/interface">https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/interface</a></p>
<p>“An interface defines a contract.”<br>“Any class, record, or struct that implements that contract must provide an implementation…”</p>
<h3 id="microsoft-learn--win32com">Microsoft Learn — Win32/COM</h3>
<p><a href="https://learn.microsoft.com/en-us/windows/win32/com/interfaces-and-interface-implementations">https://learn.microsoft.com/en-us/windows/win32/com/interfaces-and-interface-implementations</a></p>
<p>“Interfaces define a contract between an object and its clients.”</p>
<p>이건 되게 당연한 얘기다. 공식 문서에도 그냥 박혀있다.<br>근데 이상하게도 한국에서 이 본질은 정의로 잘 안 나오더라.</p>
<hr>
<h2 id="인터페이스는-왜-문제인가">인터페이스는 왜 문제인가?</h2>
<p>현실의 “계약서”를 보자.</p>
<p><strong>[결제 승인(Authorize) 서비스 계약서]</strong></p>
<p>1) <strong>정의</strong>  </p>
<ul>
<li>“승인(Authorize)”은 카드/계좌에서 <strong>금액을 예약(hold)</strong> 하는 행위이며, 즉시 매출(캡처)은 아니다.</li>
</ul>
<p>2) <strong>요청 조건(사전조건)</strong>  </p>
<ul>
<li>금액(amount)은 1 이상.  </li>
<li>통화(currency)는 <code>KRW</code>, <code>USD</code>만 허용.  </li>
<li>멱등키(idempotencyKey)는 필수이며 <strong>24시간 동안 동일 키는 동일 결과를 반환</strong>해야 한다.</li>
</ul>
<p>3) <strong>응답 조건(사후조건)</strong>  </p>
<ul>
<li>승인 성공 시 <code>authorizationId</code> 발급.  </li>
<li>승인 성공 후 <strong>10분 이내</strong> 조회하면 반드시 “APPROVED”여야 한다(최종 일관성 허용 X).  </li>
<li>실패 시 실패 사유 코드를 제공한다.</li>
</ul>
<p>4) <strong>실패/예외 조항</strong>  </p>
<ul>
<li>잔액 부족: <code>INSUFFICIENT_FUNDS</code>  </li>
<li>한도 초과: <code>LIMIT_EXCEEDED</code>  </li>
<li>중복 요청(동일 멱등키): 이전 결과 반환(오류 아님)  </li>
<li>네트워크 장애: 재시도 가능, 단 멱등키로 보호되어야 함</li>
</ul>
<p>5) <strong>비기능 조항</strong>  </p>
<ul>
<li>p95 응답시간 200ms 이하.  </li>
<li>감사 로그는 반드시 남긴다(PII 마스킹).</li>
</ul>
<p>이걸 “인터페이스”로 표현해보자.</p>
<pre><code class="language-java">public interface PaymentGateway {
    Authorization authorize(AuthorizeRequest request);
}</code></pre>
<p>이딴게 계약서?</p>
<p>계약서의 절반은 실패 조항이고, 나머지 절반은 의미/정책/시간/성능/보안인데<br>여기엔 <strong>메서드 이름이랑 타입</strong> 말고 아무것도 없다.</p>
<p>그러니까 현실에서는 결국 계약서를 <strong>쪼개서</strong> 표현한다.</p>
<hr>
<h3 id="1-인터페이스에-주석javadoc으로-계약을-우겨-넣고">1) 인터페이스에 주석(Javadoc)으로 계약을 우겨 넣고</h3>
<pre><code class="language-java">public interface PaymentGateway {

    /**
     * Authorize reserves funds (not capture).
     *
     * Contract:
     * - request.amount MUST be &gt;= 1
     * - request.currency MUST be KRW or USD
     * - request.idempotencyKey MUST be provided
     * - Same idempotencyKey must return the same result for 24 hours
     * - On success: returns APPROVED with authorizationId
     * - Failure codes: INSUFFICIENT_FUNDS, LIMIT_EXCEEDED, ...
     * - Consistency: approved authorization must be readable as APPROVED within 10 minutes
     */
    Authorization authorize(AuthorizeRequest request);

    Authorization getAuthorization(String authorizationId);
}</code></pre>
<hr>
<h3 id="2-노션마크다운에-요구사항정의서를-따로-만들고-사람이-읽어야만-아는-계약">2) 노션/마크다운에 “요구사항정의서”를 따로 만들고 (사람이 읽어야만 아는 계약)</h3>
<pre><code class="language-md"># PaymentGateway 계약서 v1.3 (요구사항/계약 문서)

## 1. 요청 계약
- amount는 1 이상
- currency는 KRW/USD만 허용
- idempotencyKey는 필수
- 동일 idempotencyKey는 24시간 동일 결과 반환 (중복 요청은 오류 아님)

## 2. 응답/실패 계약
- 승인 성공: status=APPROVED + authorizationId 발급
- 승인 실패: status=DECLINED + failureCode 제공
- failureCode: INSUFFICIENT_FUNDS / LIMIT_EXCEEDED / NETWORK_ERROR ...

## 3. 일관성 계약
- 승인 성공 후 10분 이내 조회 시 반드시 APPROVED
- 최종 일관성으로 PENDING이 잠깐 보이는 건 허용하지 않음

## 4. 비기능 계약
- p95 응답시간 200ms 이하
- 감사 로그 필수, PII 마스킹</code></pre>
<hr>
<h3 id="3-그리고-마지막으로-테스트로-검증한다-계약-위반하면-빌드가-깨지게">3) 그리고 마지막으로 테스트로 “검증”한다 (계약 위반하면 빌드가 깨지게)</h3>
<pre><code class="language-java">import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.*;

class PaymentGatewayContractTest {

    private final PaymentGateway gateway = new RealPaymentGateway(/* ... */);

    @Test
    void idempotency_same_key_returns_same_result_for_24h() {
        AuthorizeRequest req = new AuthorizeRequest(
                1000,
                &quot;KRW&quot;,
                &quot;idem-123&quot;
        );

        Authorization first = gateway.authorize(req);
        Authorization second = gateway.authorize(req);

        // “중복 요청은 오류가 아니라 동일 결과 반환”이라는 계약 검증
        assertThat(second).isEqualTo(first);
    }
}</code></pre>
<hr>
<p>결국 현실의 계약서 조항은 소프트웨어로 내려오면 이렇게 <strong>삼분할</strong>된다.</p>
<ul>
<li><strong>인터페이스(Java)</strong>: 컴파일러가 강제 가능한 최소 형태(메서드 존재, 타입)  </li>
<li><strong>문서(Notion/Javadoc/Markdown)</strong>: 의미/정책/예외/비기능 요구사항(읽어야만 앎)  </li>
<li><strong>테스트(JUnit)</strong>: 계약을 실제로 “검증/집행”하는 장치(깨지면 빌드 실패)</li>
</ul>
<p>이게 왜 문제냐?</p>
<p>인터페이스가 계약이라는 사실을 정의 못하니<br>테스트가 “계약을 검증하는 문서”라는 사실도 같이 못 본다.</p>
<p>그러니까 TDD 같은 걸 “방법론”으로 외워서 이상하게 숭배하고,<br>정작 본질(계약 검증)은 놓치고,<br>그 상태로 또 다음 세대에게 전파한다.</p>
<p><strong>테스트는 계약을 검증하는 살아있는 검증문서다.</strong><br>여기서부터 시작해야 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TDD를 부숴주마 - 개요]]></title>
            <link>https://velog.io/@sour_grape/TDD%EB%A5%BC-%EB%B6%80%EC%85%94%EC%A3%BC%EB%A7%881-%EA%B0%9C%EC%9A%94</link>
            <guid>https://velog.io/@sour_grape/TDD%EB%A5%BC-%EB%B6%80%EC%85%94%EC%A3%BC%EB%A7%881-%EA%B0%9C%EC%9A%94</guid>
            <pubDate>Tue, 10 Feb 2026 03:51:43 GMT</pubDate>
            <description><![CDATA[<h1 id="개요">개요</h1>
<p>유튜브나 인터넷에 TDD 자료를 보다가 열 받아서 만들었다
TDD는 구리다, 왜 구린지 시리즈에서 설명하고 테스트의 목적
TDD의 올바른 해석, 내가 생각하는 좋은 테스트 순서대로 연재하겠다.</p>
<p>시리즈 초반엔 테스트와 관계 없어 보이는 내용이 포함되는데 인내심을 가지고 시리즈를 정독하면 이해하게 될 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[1. 저작권을 설정하자]]></title>
            <link>https://velog.io/@sour_grape/1.-%EC%A0%80%EC%9E%91%EA%B6%8C%EC%9D%84-%EC%84%A4%EC%A0%95%ED%95%98%EC%9E%90</link>
            <guid>https://velog.io/@sour_grape/1.-%EC%A0%80%EC%9E%91%EA%B6%8C%EC%9D%84-%EC%84%A4%EC%A0%95%ED%95%98%EC%9E%90</guid>
            <pubDate>Thu, 09 Oct 2025 06:07:03 GMT</pubDate>
            <description><![CDATA[<h1 id="오픈소스-저작권">오픈소스 저작권</h1>
<p>당연히 오픈소스 프레임워크를 만드는데 저작권이 중요하다.</p>
<p>나는 인간을 믿지 않으며 인간은 짐승과 같다는 생각을 하기 때문에 저작권도 방어적으로 초반에 설정할 것이다.</p>
<h2 id="apache-20-license">Apache-2.0 license</h2>
<p>Apache-2.0 license는 절충안이다. MIT license는 전부 여는것이기 때문에 오픈소스 그대로 퍼다가 이름만 바꿔도 못 막는다.</p>
<p>그런 사람들이 당연히 있지, 악의적인 그룹, 기업들이 이름 바꿔서 운영하면 개인은 밀릴 수 있기 때문에 최소한의 안전장치는 해야한다.</p>
<p>Apache license를 해두면 MIT와 달리 원저작자 표시 의무가 있기 때문에 어느정도 오픈소스답고 저작권도 보호해주는 방안이다.</p>
<h2 id="clacontributor-license-agreement">CLA(Contributor License Agreement)</h2>
<h3 id="copyright-assignment">Copyright Assignment</h3>
<p>apache를 해놓은다고 안심하면 안 된다. 세상엔 악의적인 도둑들이 많으니
기여자에게 CLA 서명을 받아야한다. CLA 서명엔 2가지가 있다.</p>
<p>Copyright Assignment (저작권 양도형), License Grant (사용 허락형)</p>
<p>전자는 contributor가 나에게 자신이 기여한 코드의 저작권을 양도하는거고 후자는 기여자가 저작권을 가진 방안이다.</p>
<p><strong>기여자가 저작권을 주장 안 할까?</strong></p>
<p>당연히 하지</p>
<p>법적 문제로 걸고 넘어지면 아주 귀찮게 되는거다. 그리고 이 프레임워크는 사실상 내 철학이 강하게 반영되었기 때문에 contributing이 사소한 오픈소스다.</p>
<p>그래서 거의 모든 뼈대와 코드를 내가 작성하기 때문에 
<strong>Copyright Assignment</strong>를 봇으로 걸어놨다.</p>
<h2 id="깃허브-bot-설정을-하자">깃허브 bot 설정을 하자</h2>
<p>오픈소스에 기여할 때 PR에 CLA 서명을 강제하자</p>
<p>1️⃣ CLA Assistant GitHub 앱 설치</p>
<ol>
<li><p>접속 👉 <a href="https://cla-assistant.io/">https://cla-assistant.io/</a></p>
</li>
<li><p>오른쪽 상단 “Sign in with GitHub” 클릭 → 깃허브 로그인</p>
</li>
<li><p>로그인 후 “Add a new repository” 버튼 클릭</p>
</li>
<li><p>연동할 저장소 선택</p>
</li>
<li><p>GitHub 권한 요청 창 뜨면 “Install &amp; Authorize” 선택</p>
</li>
</ol>
<p>📌 설치 후, CLA Assistant가 해당 리포에 대한 GitHub App으로 등록됨.</p>
<p>위처럼 봇을 설정하면 PR을 날릴 때 서명하지 않으면 요청이 안 되게 막을 수 있다.</p>
<h2 id="cla-문서를-박아놓자">CLA 문서를 박아놓자</h2>
<p>CLA.md 문서를 프로젝트 루트에 만들어놓자. </p>
<pre><code class="language-md">This Contributor License Agreement (&quot;Agreement&quot;) is made between Seong Gyeongjun (GitHub ID: B-Singularity) (&quot;Project Owner&quot;) and the individual who signs this Agreement (&quot;Contributor&quot;).

By signing this Agreement, you accept and agree to the following:

1. **Assignment of Copyright**  
   You hereby assign to the Project Owner all right, title, and interest in and to your contributions submitted to the “Kontrakt” project, including copyright and related rights.

2. **License Grant Back to Contributor**  
   The Project Owner grants you a perpetual, worldwide, royalty-free license to use, reproduce, and distribute your contributions for any purpose.

3. **Representations and Warranties**  
   You represent that your contributions are your original work and that you have the right to grant this assignment.

4. **Governing Law**  
   This Agreement shall be governed by the laws of the Republic of Korea.

Signed electronically via GitHub by submitting a pull request to the Project.</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[0. 들어가기]]></title>
            <link>https://velog.io/@sour_grape/0.-%EB%93%A4%EC%96%B4%EA%B0%80%EA%B8%B0</link>
            <guid>https://velog.io/@sour_grape/0.-%EB%93%A4%EC%96%B4%EA%B0%80%EA%B8%B0</guid>
            <pubDate>Thu, 09 Oct 2025 05:46:33 GMT</pubDate>
            <description><![CDATA[<h1 id="들어가기">들어가기</h1>
<p>내 생각엔 개발자는 각자의 철학을 가져야한다.
개발을 공부하며 요구사항, 계약, 객체지향이 중요하다고 생각하게 되었다.</p>
<p>이 세계관을 가지고 있기 때문에 모든 코드엔 내 철학이 묻게 되었다.</p>
<p>어떤 세계관을 가지고 있는지는 저번 시리즈를 통해서 밝혔으니 참고바란다.</p>
<blockquote>
<p><a href="https://velog.io/@sour_grape/series/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%ED%95%B4%EC%84%9D">https://velog.io/@sour_grape/series/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%ED%95%B4%EC%84%9D</a></p>
</blockquote>
<p>내 세계관의 기본 뼈대인 살아있는 문서를 작성하는 프레임워크를 만들려고 한다.</p>
<p>이 시리즈는 프레임워크를 만드는 동안 개고생한 일지가 될 거고 누구든 프레임워크를 만들때 참고용 자료 정도로 봤으면 좋겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[객체지향 해석(7) - DI는 별거없다]]></title>
            <link>https://velog.io/@sour_grape/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%ED%95%B4%EC%84%9D7-DI%EB%8A%94-%EB%B3%84%EA%B1%B0%EC%97%86%EB%8B%A4</link>
            <guid>https://velog.io/@sour_grape/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%ED%95%B4%EC%84%9D7-DI%EB%8A%94-%EB%B3%84%EA%B1%B0%EC%97%86%EB%8B%A4</guid>
            <pubDate>Tue, 24 Jun 2025 08:32:04 GMT</pubDate>
            <description><![CDATA[<h1 id="요구사항-계약-메시지">요구사항, 계약, 메시지</h1>
<p>객체지향을 설명하면서 강조했던 세 가지이다.</p>
<p>세 가지가 독립적인 게 아니고 유기적으로 연결되어 있다는 걸 이제는 잘 알 것이다.
이 글 시리즈의 내용 또한 세 가지의 유기적인 흐름을 그대로 보여주었을 뿐이다.</p>
<p>인터페이스와 다형성은 그냥 갈아끼우기라고 했다.
근데 갈아끼우는 게 정말 쉬울까? 함정이 섞여 있는데 오늘은 그걸 좀 다뤄보고 <strong>DI가 함정을 객체지향이 하던 대로 극복한 기술이란 걸 알려줄 것이다.</strong></p>
<h1 id="지금까지-한-걸-설계해보자">지금까지 한 걸 설계해보자</h1>
<p>이 세 개의 의미는 알겠으니 코드단에서 어떻게 설계에 반영되는지 보자.
앞선 시리즈에서 한 그대로 하겠다.</p>
<ol>
<li>요구사항과 계약: &quot;야 차 타고 와&quot;
지켜야 할 계약은 두 가지이다. 약속 장소까지 올 것, 그리고 차를 타고 올 것.</li>
</ol>
<p>그렇다면 계약은 인터페이스로 다음과 같이 설계한다.</p>
<pre><code class="language-java">interface Car {
    void move();
}</code></pre>
<pre><code class="language-java">class Friend {
    private final Car car;

    // 친구는 차를 타고 와야한다는 계약을 넣어준다.
    public Friend(Car car) {
        this.car = car;
    }

    public void goToAppointment() {
        this.car.move();
    }
}</code></pre>
<p>여기까진 자연스럽게 잘 설계했다. 저번 예처럼 제네시스 GV80 COUPE BLACK 차량번호 123가 4568을 설계에 직접 넣은 게 아닌, 우리가 평소 말하듯이 그냥 클래스에 차 인터페이스를 자연스럽게 넣어 설계했다. 아주 좋다.</p>
<ol start="2">
<li>이제 메시지를 보내서 니가 알아서 하라고 하자.</li>
</ol>
<p>계약만 지키고 알아서 하라는 메시지를 보내야 한다.</p>
<h1 id="설계한-걸-써야-하는데">설계한 걸 써야 하는데?</h1>
<p>이제 자연스럽게 객체지향으로 설계한 걸 써야 한다. 설계를 왜 했겠나? 당연히 쓰려고 만든 거지. 내가 친구한테 차 타고 오라는 실제 상황을 구현하자.</p>
<pre><code class="language-java">public class Me {
    public static void main(String[] args) {
        // &quot;나&quot;는 친구를 약속에 오게 하고 싶습니다.
        // 그런데... 친구에게 줄 &#39;차&#39;를 &#39;나&#39; 자신이 직접 만들어야 합니다.
        Car aFriendsCar = new MyGenesis();

        // 그리고 그 차를 가진 친구를 만듭니다.
        Friend friend = new Friend(aFriendsCar);

        // 이제야 친구에게 약속 장소로 오라고 말할 수 있습니다.
        friend.goToAppointment();
    }
}</code></pre>
<p>친구한테 메시지를 보내야 한다고 했다.
다시 한번 강조하는데 객체지향은 다음과 같다.</p>
<p><strong>&quot;요구사항과 계약만 지키면 되니까 니가 알아서 해와.&quot;</strong></p>
<p>앞의 예에서 요구사항과 계약을 잘 지켰고, 메시지의 역할을 하는 메서드도 인터페이스를 넣어서 잘 만들었다. &quot;이제 니가 알아서 해오라는&quot; 메시지를 잘 보낼 수 있을 것 같다. 근데 이 메시지를 보내는 과정에 함정이 존재한다.</p>
<p>위의 코드에선 내가 친구한테 메시지를 보내기 위해선 구체적인 차 정보를 내가 주입해 줘야 한다.</p>
<p>이건 내가 제네시스 GV80 COUPE BLACK 차량번호 123가 4568이 친구 차임을 아는 상황과 같다.</p>
<p><strong>즉 이 코드는 결국엔 내가 &quot;야 니가 가진 제네시스 GV80 COUPE BLACK 차량번호 123가 4568 타고 와&quot;라고 한 상황과 똑같다.</strong></p>
<p>니가 알아서 해오라는 객체지향을 수고를 들여서 잘 설계했는데, 막상 실행할 때 전부 망쳐버리는 최악의 상황이다.</p>
<h1 id="이걸-해결하는-법은">이걸 해결하는 법은?</h1>
<p><strong>객체지향이 원래 이런 상황을 해결하는 방법이 뭐지? 앞 시리즈에서 말했듯, 니가 알아서 해오라는 메시지를 보내는 것이다.</strong></p>
<p>위 코드에선 마지막에 행동을 할 친구를 만들어줄 때 친구의 구체적인 차 정보를 내가 주입시켜줘서 망한 상황이다. 즉, 내가 주입해서 망한 상황이다.</p>
<p>그럼 내가 생성자를 주입 안 하고 알아서 주입해오라는 메시지를 보내면 간단하게 해결되는 거 아닌가?</p>
<p>그럼 생성자만 주입하는 놈을 만들어서 그놈한테 명령하자. &quot;야 니가 알아서 주입해라.&quot;</p>
<p>그럼 이렇게 표현할 수 있다.</p>
<pre><code class="language-java">public class FriendCar {
    // &quot;차가 필요하니 하나 준비해줘&quot; 라는 요청에 대한 응답
    public Car provideCar() {
        // 친구차가 뭔지는 이 놈만 안다.
        return new MyGenesis();
    }
}</code></pre>
<p>그럼 나는 이렇게 메시지를 보낼 수 있다.</p>
<pre><code class="language-java">public class Me {
    public static void main(String[] args) {
        // 친구차가 뭔지 알고있는 놈을 부른다.
        FriendCar friendcar = new FriendCar();

        // 그 놈한테 알아서 구체적인 건 다 맡긴다.
        //    &#39;나&#39;는 이제 이게 &#39;MyGenesis&#39;인지 전혀 모른다. 그냥 &#39;Car&#39;라고만 알고 있다.
        Car aFriendsCar = friendcar.provideCar();

        // 뭔 차인지는 모르겠고 그냥 친구한테 &quot;야 차 타고와&quot; 메세지를 보낸다.
        Friend friend = new Friend(aFriendsCar);

        // 그럼 친구가 알아서 차 타고온다.
        friend.goToAppointment();
    }
}</code></pre>
<p>내가 친구 차를 알아버리는 함정을 객체지향의 원래 해결 방법으로 해결했다.
친구 차 정보를 주입해주는 놈을 만들어서 니가 알아서 해오라고 메시지만 보냈다.</p>
<p>&quot;야 니가 가진 제네시스 GV80 COUPE BLACK 차량번호 123가 4568 타고 와&quot;가 아닌, &quot;야 차 타고 와&quot;라는 메시지를 완벽하게 구현한 것이다.</p>
<h1 id="결론">결론</h1>
<p>DI를 설명할 때 제어의 역전<del>~ 결합도를 낮춰서</del> 뭐라뭐라 어렵게 설명하는데,</p>
<p>그냥 함정을 피한 객체지향이 맨날 하던 방법이다. 별거 아니다. 그냥</p>
<p>마지막까지 &quot;니가 알아서 해오라&quot;는 태도를 고수하는 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[객체지향 해석(6) - 인터페이스, 다형성? 그냥 갈아 끼우기]]></title>
            <link>https://velog.io/@sour_grape/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%ED%95%B4%EC%84%9D6-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%EB%8B%A4%ED%98%95%EC%84%B1-%EA%B7%B8%EB%83%A5-%EA%B0%88%EC%95%84-%EB%81%BC%EC%9A%B0%EA%B8%B0</link>
            <guid>https://velog.io/@sour_grape/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%ED%95%B4%EC%84%9D6-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%EB%8B%A4%ED%98%95%EC%84%B1-%EA%B7%B8%EB%83%A5-%EA%B0%88%EC%95%84-%EB%81%BC%EC%9A%B0%EA%B8%B0</guid>
            <pubDate>Mon, 23 Jun 2025 15:54:09 GMT</pubDate>
            <description><![CDATA[<h1 id="파인만을-좋아함">파인만을 좋아함</h1>
<p>나는 파인만을 좋아한다. 물리학과를 나오면 파인만의 빨간책이란 걸 읽는데 읽다보면 그의 전기도 읽는게 보통이다. 파인말 왈 쉽게 표현하면 되는데 뭐하러 어렵게 표현하나.</p>
<p>그래서 파인만은 이론도 그림으로 다이어그램 만들어서 노벨상탐</p>
<p>아무튼 내가 하고 싶은 말은 객체지향 설명하는데 뭐하러 어렵게 설명하는지 이해가 안된다는 말이다.</p>
<h1 id="인터페이스의-장점">인터페이스의 장점</h1>
<p>인터페이스의 가장 큰 장점은 계약을 소프트웨어적으로 명시하고 강제한다는 점을 강조해왔다. 그 외 좋은점엔 다형성이 있다.</p>
<p>그 전 글에 추상화를 설명하면서 자연스럽게 설명했다.</p>
<p>계약서만 작성하면 그걸 어떻게 구현하든 상관없다는 말이다. 전 글의 비유를 다시 가져오면</p>
<p>&quot;야 차타고 와&quot; 이 계약은 그냥 차에 해당되면 상관없다는 말이다.
내가 택시를 타든, 버스를 타든 내가 알아서 차만 타고 오면 된다는 점이다.
그럼 반대로 생각해보면 내가 차만 타면 되니까 맘대로 바꿔도 되겠네?</p>
<p>원래는 버스를 탈려고 했는데 시간이 늦어서 택시를 타도 상관없다. 내 사정상 맘대로 바꿀 수 있다는 점. 내 맘대로 갈아끼울수 있다는 점이 바로 다형성이다.</p>
<h1 id="다형성이-나오는-이유는-객체지향과-메세지">다형성이 나오는 이유는 객체지향과 메세지</h1>
<p><strong>객체지향의 핵심은 철저한 분업화라고 했다.</strong> 각자의 책임만 지고 상대방에 관심없이 메세지만 보내면 되는 방식, ** 계약만 던져주고 니가 알아서 해와**란 방식에서 다형성이 나오는 것이다.</p>
<p>*<em>내 맘대로? 계약만 지키면 내 맘대로 할게, 나중에 맘에 안들면 내가 알아서 갈아 끼울게 니는 결과만 받아. 그게 계약이니까 *</em></p>
<p>내 맘대로 갈아끼우고 바꾸고 구현해도 계약만 지키서 결과물만 던져주면 철저한 분업화고 성공한 메세지이고 옳은 객체지향이다.</p>
<h1 id="다형성의-장점">다형성의 장점</h1>
<p>프로그래밍에서 다형성의 장점을 그대로 보여주겠다</p>
<pre><code class="language-java">interface Car {
    void moveTo(String destination);
}</code></pre>
<pre><code class="language-java">class Person {
    // anyCar 즉, 차면 아무거나 됨
    public void goToAppointment(Car anyCar) {
        anyCar.moveTo(&quot;약속 장소&quot;);
    }
}</code></pre>
<pre><code class="language-java">public class Main {
    public static void main(String[] args) {
        // 약속에 가야 하는 &#39;나&#39;
        Person me = new Person();

        // 타고 싶은거 내가 맘대로 만들어서 이용하면 됨
        Car myCar = new MyGenesis();
        Car taxi = new Taxi();
        Car bus = new Bus();

        System.out.println(&quot;--- 오늘의 계획 ---&quot;);

        // &quot;차 타고 와&quot; 라는 약속(계약)만 지키면 된다.
        // 어떤 &#39;차&#39;를 사용할지는 내 마음대로

        System.out.println(&quot;1. 오늘은 내 차로 가볼까?&quot;);
        me.goToAppointment(myCar); // 자가용 탈거임

        System.out.println(&quot;2. 내일은 택시를 타야지.&quot;);
        me.goToAppointment(taxi); // 택시 타야지

        System.out.println(&quot;3. 모레는 버스를 타자.&quot;);
        me.goToAppointment(bus); // 버스 타야지
    }
}</code></pre>
<p>다형성의 장점이 유연하니, 확장성이 있니 이런 설명이 많은데 이게 더 직관적이지 않나?
계약 사항만 지키면 구현은 내 맘대로 할거니까 당연히 유연하고, 확장성이 있지.</p>
<p>아니 쉽게 말해서 내 맘대로 갈아끼워도 아무 문제가 없는게 가장 큰 장점이다.</p>
<p>프로그래밍에서 나중에 코드를 바꾸고 싶은경우는 크게 2가지</p>
<ol>
<li>요구사항과 계약을 바꾼다.</li>
<li>구현체를 바꾼다.</li>
</ol>
<p>1번은 그냥 인터페이스를 바꾸면 되는것
2번은 그냥 내 맘대로 구현체를 만들어서 원래 있던거 치우고 갈아끼우기만 하면 되는것</p>
<p>2번에 해당하는 게 다형성이라고 생각한다.</p>
<h1 id="결론">결론</h1>
<p>다형성은 객체지향의 분업화와 메세지에서 나오는 것이다. <strong>계약만 지키면 내 맘대로 갈아끼우는게 다형성이다.</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[객체지향 해석(5) - 추상화??]]></title>
            <link>https://velog.io/@sour_grape/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%ED%95%B4%EC%84%9D5-%EC%B6%94%EC%83%81%ED%99%94</link>
            <guid>https://velog.io/@sour_grape/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%ED%95%B4%EC%84%9D5-%EC%B6%94%EC%83%81%ED%99%94</guid>
            <pubDate>Mon, 23 Jun 2025 08:07:36 GMT</pubDate>
            <description><![CDATA[<h1 id="추상화">추상화??</h1>
<p>추상화를 대표하는 학문은 수학이다. 수학의 추상화를 즐길 수 있다면 그것이야말로 수학 재능이다.
수학의 추상화가 너무 악명이 높기 때문에 프로그래밍의 추상화 또한 어려울 것이라고 생각한다.</p>
<p>수학의 추상화와 프로그래밍의 추상화는 같다. 하지만 전혀 다르다.</p>
<h1 id="프로그래밍의-추상화는-우리의-일상이다">프로그래밍의 추상화는 우리의 일상이다</h1>
<p>프로그래밍의 추상화는 우리의 일상이고 우리의 언어이다.</p>
<p>프로그래밍의 추상화가 가리키는 의미는 우리가 평소에 대화하는 것과 같고, 정확하게 말하면 내가 중요하게 생각하는 계약 자체를 표현하는 방법이다.</p>
<p>예를 들어서 내가 약속을 잡았다고 치자. 그럼 상대방에게 &quot;내일 차 타고 와&quot; 이런 말을 할 수 있다. 굉장히 흔한 말이지? 근데 이게 프로그래밍의 추상화다.</p>
<p>계약: 내일 약속 장소에 도착해야 한다, 차를 타고 와야 한다.</p>
<p>두 가지 계약이 생겼다. 따라서 우리가 지켜야 하는 건 두 가지뿐이다. 내일 약속 장소에 갈 것, 차를 탈 것. 이 두 가지만 지키면 구체적인 방법은 내 맘대로 해도 된다는 말이다.</p>
<p>내가 내일 택시를 타든, 버스를 타든, 자가용을 타든 상대방은 상관하지 않는다는 말이다.</p>
<p>반대로 프로그래밍의 추상화를 사용하지 않는다는 건 뭘까?</p>
<p>상대방이 &quot;내일 니가 가지고 있는 제네시스 GV80 COUPE BLACK 차량번호 123가 4568을 타고 와라&quot; 이 말과 같다.</p>
<p>평소에 이렇게 대화하면 미친놈 취급받는다. 또 이 대화의 가장 큰 문제점은 다음과 같다. 저걸 계약으로 생각하면 나는 꼭 내가 가지고 있는 제네시스 GV80 COUPE BLACK 차량번호 123가 4568을 타고 가야 한다는 점이다.</p>
<p>근데 내가 폐차했으면? 차를 수리를 맡겨서 못 탄다면? 새 차로 바꿨다면?
내가 차를 다른 지방에 두고 왔다면? 저 계약 자체를 지키기 힘들고, 또 저 계약에서 중요한 건 약속 장소에 오는 건데 사소한 제약 때문에 계약을 못 지킨다.</p>
<p>이건 상대방이 내가 어떤 차를 가지고 있는지 알고 있기 때문에 발생하는 일이다.</p>
<p>계약엔 차만 포함되고 니가 알아서 와라. 이게 보통 우리가 대화하는 방식이다.</p>
<p>화상 통화하게 노트북 켜라, 밥 먹게 숟가락 놔라, 카톡 좀 봐, 차 고장 났으니 수리점 가라. 이런 일상적인 언어는 다 추상화다.</p>
<p>우리가 일상에서 사용하는 굉장히 자연스러운 언어이다.
근데 저걸, 화상 통화하게 니가 가지고 있는 M2 MacBook Air 켜라, 밥 먹게 니가 가지고 있는 중국집 숟가락 놔라, 니가 가지고 있는 아이폰 14로 카톡 좀 켜서 봐라, 니가 가지고 있는 제네시스 GV80 COUPE BLACK 차량번호 123가 4568이 고장 났으니 블루핸즈 하남현대서비스에 가서 수리해라.</p>
<p>이런 식으로 말하는 건 추상화가 아닌 쓸데없이 구체적으로 계약을 잡는 것이다.
나중에 바뀌거나 사정이 생기면 이룰 수 없는 계약이고, 그건 둘째 치고 저렇게 대화하는 사람이 있겠나?</p>
<p>근데 프로그래밍에선 저런 식으로 대화하는 실수를 저지르곤 한다.</p>
<p>위에서 미친놈 취급받는 대화를 코드로 옮겨보자.</p>
<pre><code class="language-java">// 구체적인 구현체: 제네시스 GV80
class GenesisGv80 {
    public void driveTo(String destination) {
        System.out.println(&quot;제네시스 GV80을 타고 &quot; + destination + &quot;(으)로 부드럽게 이동합니다.&quot;);
    }
}

// 요청자: 구체적인 &#39;GenesisGv80&#39;만 요구함
class Person {
    public void goToAppointment(GenesisGv80 myCar) {
        // 이 코드는 오직 &#39;제네시스 GV80&#39;하고만 대화할 수 있습니다.
        myCar.driveTo(&quot;약속 장소&quot;);
    }
}

// 실행 코드
public class Main {
    public static void main(String[] args) {
        Person person = new Person();
        GenesisGv80 genesis = new GenesisGv80();
        person.goToAppointment(genesis);
    }
}</code></pre>
<p>위처럼 코드를 짜는 건 굉장히 자연스럽게 받아들여진다. 이상하지 않은가? 일상의 대화를 코드로 옮겼을 뿐인데, 말로 할 땐 미친놈 취급받는 게 프로그래밍에선 자연스럽다는 게?</p>
<p>자연스러운 일상의 대화로 바꿔보면</p>
<pre><code class="language-java">// 1. 계약서(Interface) 정의: &quot;이동한다&quot;는 계약
interface Car {
    void moveTo(String destination);
}

// 2. 계약을 이행하는 다양한 구현체들
class MyGenesis implements Car {
    @Override
    public void moveTo(String destination) {
        System.out.println(&quot;내 제네시스를 타고 &quot; + destination + &quot;(으)로 이동합니다.&quot;);
    }
}

class Taxi implements Car {
    @Override
    public void moveTo(String destination) {
        System.out.println(&quot;카카오 택시를 호출해서 &quot; + destination + &quot;(으)로 이동합니다.&quot;);
    }
}

class Bus implements Car {
    @Override
    public void moveTo(String destination) {
        System.out.println(&quot;143번 버스를 타고 &quot; + destination + &quot;(으)로 이동합니다.&quot;);
    }
}

// 3. 요청자: &#39;Car&#39;라는 계약에만 의존함
class Person {
    public void goToAppointment(Car anyCar) {
        // 이 코드는 &#39;Car&#39; 계약을 지키는 그 어떤 객체와도 대화할 수 있습니다.
        anyCar.moveTo(&quot;약속 장소&quot;);
    }
}

// 4. 실행 코드
public class Main {
    public static void main(String[] args) {
        Person person = new Person();

        // 오늘은 내 차를 이용
        person.goToAppointment(new MyGenesis());

        // 내일은 택시를 이용
        person.goToAppointment(new Taxi());

        // 모레는 버스를 이용
        person.goToAppointment(new Bus());
    }
}</code></pre>
<p>위 코드로 짜면 잘 짰지만 귀찮다고 생각이 든다. 추상화를 하면 노력이 배가 드니까 귀찮아서 그냥 구체적인 코드로 짜고 싶은 생각이 든다.
하지만 이 코드는 자연스러운 일상을 옮긴 코드다. 같은 개념을 다르게 생각하는 게 나는 이상하다고 생각한다.</p>
<h1 id="결론">결론</h1>
<p>추상화는 우리가 맨날 쓰는 자연스러운 언어이다.
다음 시간엔 이런 추상화가 어떤 장점이 있는지 설명하겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[객체지향 해석(4) - 계약과 TDD]]></title>
            <link>https://velog.io/@sour_grape/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%ED%95%B4%EC%84%9D4-%EA%B3%84%EC%95%BD%EA%B3%BC-TDD</link>
            <guid>https://velog.io/@sour_grape/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%ED%95%B4%EC%84%9D4-%EA%B3%84%EC%95%BD%EA%B3%BC-TDD</guid>
            <pubDate>Sun, 22 Jun 2025 16:10:05 GMT</pubDate>
            <description><![CDATA[<h1 id="tdd를-싫어하는-이유">TDD를 싫어하는 이유</h1>
<p>보통 TDD는 다음과 같은 절차를 가진다.</p>
<ol>
<li>RED: 실패하는 테스트 코드를 먼저 작성한다.</li>
<li>GREEN: 테스트 코드를 성공시키기 위한 실제 코드를 작성한다.</li>
<li>BLUE: 중복 코드제거, 일반화등의 리팩터링을 실행한다.</li>
</ol>
<p>내가 TDD를 싫어하는 이유다.</p>
<p>하지만 나는 TDD를 사랑한다. 무슨 헛소리냐 </p>
<p>나는 TDD의 본질이 저 방식이라고 생각 안한다.</p>
<p>이 시리즈의 1, 2, 3편을 봤다면 요구사항과 계약을 지키고 구현하는 게 중요함을 알 것이다.</p>
<p>우리는 계약을 소프트웨어에서 인터페이스로 적용한다.
그런데 그 계약을 실제로 지키는지 어떻게 알지?</p>
<p>인터페이스를 따른다고 내가 원하는 요구사항을 만족한건가?</p>
<p>부족하다.</p>
<p>보통 요구사항과 계약을 구현하고 지키는지 테스트 하는데
결국엔 계약을 지켜야되는게 목표가 아닌가?</p>
<p>그럼 계약을 지키는지 검사하는 테스트를 먼저 만들면 되는거 아닌가?</p>
<p>TDD는 계약의 검증이다. TDD는 구현이 계약을 만족하는지 검사하는 진정한 의미의 테스트이다.</p>
<p>테스트를 통과하지 못한다면 그건 실패한 구현이다. 왜? 계약을 지키지 못했으니</p>
<p>내가 왜 TDD를 싫어하는지 이해가 되나? TDD는 계약의 검증이다. 살아있는 보증서이고 변화하는 문서이다. 근데 일부러 틀리는 코드를 작성한다?</p>
<p>TDD의 &#39;RED&#39; 단계는 실패를 목표로 삼는 것이 아니다. 단지 &#39;지켜야 할 계약(테스트)&#39;을 먼저 정의했기 때문에, 아직 그 계약을 만족시키는 구현체가 없어 &#39;자연스럽게&#39; 실패 상태로 시작하는 것뿐이다. &#39;실패&#39;는 목표가 아니라, 계약 이행 전의 당연한 상태에 불과하다. 이를 &#39;일부러 틀린다&#39;고 표현하는 것은 TDD의 본질을 왜곡하고 주객을 전도시키는 설명 방식이다.</p>
<h1 id="tdd를-사랑하는-이유">TDD를 사랑하는 이유</h1>
<p>이 시리즈의 1, 2, 3편을 읽었다면 내가 요구사항과 계약을 중요하게 생각함을 이해할 것이다.</p>
<p>테스트를 먼저 작성하든, 구현을 하고 작성하든 계약을 검증한다는 점에서 나는 TDD를 사랑한다. 나는 아래와 같은 방식으로 TDD를 이용하고 있다.</p>
<p>이해하기 쉽게 내가 VO 클래스를 단위 테스트한 파일을 보여주겠다.</p>
<pre><code class="language-python">class TestActorValueObject(unittest.TestCase):

    def test_create_valid_actor_all_fields(self):
        # 계약: 모든 필드가 유효한 값으로 ActorVO를 성공적으로 생성할 수 있어야 한다.
        vo = ActorVO(name=&quot;톰 행크스&quot;, role_name=&quot;포레스트 검프&quot;, external_id=&quot;tmdb_actor_123&quot;)
        self.assertEqual(vo.name, &quot;톰 행크스&quot;)
        self.assertEqual(vo.role_name, &quot;포레스트 검프&quot;)
        self.assertEqual(vo.external_id, &quot;tmdb_actor_123&quot;)

    def test_create_valid_actor_name_only(self):
        # 계약: 필수 필드인 이름만으로 ActorVO를 성공적으로 생성할 수 있어야 한다.
        vo = ActorVO(name=&quot;송강호&quot;)
        self.assertEqual(vo.name, &quot;송강호&quot;)
        self.assertIsNone(vo.role_name)
        self.assertIsNone(vo.external_id)

    def test_create_valid_actor_name_and_role(self):
        # 계약: 이름과 배역명으로 ActorVO를 성공적으로 생성할 수 있어야 한다.
        vo = ActorVO(name=&quot;최민식&quot;, role_name=&quot;이순신&quot;)
        self.assertEqual(vo.name, &quot;최민식&quot;)
        self.assertEqual(vo.role_name, &quot;이순신&quot;)
        self.assertIsNone(vo.external_id)

    def test_create_with_empty_name_raises_value_error(self):
        # 계약: 비어있는 이름으로 생성 시도 시 ValueError가 발생해야 한다.
        with self.assertRaisesRegex(ValueError, &quot;배우 이름은 비어있을 수 없습니다.&quot;):
            ActorVO(name=&quot;&quot;)

    def test_create_with_name_too_long_raises_value_error(self):
        # 계약: 이름이 최대 길이(100자)를 초과하면 ValueError가 발생해야 한다.
        with self.assertRaisesRegex(ValueError, &quot;배우 이름은 유효한 문자열이어야 하며 최대 100자입니다.&quot;):
            ActorVO(name=&quot;가&quot; * 101)

    def test_create_with_role_name_too_long_raises_value_error(self):
        # 계약: 배역명이 최대 길이(100자)를 초과하면 ValueError가 발생해야 한다.
        with self.assertRaisesRegex(ValueError, &quot;배역명은 최대 100자까지 가능합니다.&quot;):
            ActorVO(name=&quot;배우이름&quot;, role_name=&quot;가&quot; * 101)

    def test_create_with_external_id_too_long_raises_value_error(self):
        # 계약: 외부 ID가 최대 길이(100자)를 초과하면 ValueError가 발생해야 한다.
        with self.assertRaisesRegex(ValueError, &quot;외부 ID는 최대 100자까지 가능합니다.&quot;):
            ActorVO(name=&quot;아이디테스트배우&quot;, external_id=&quot;a&quot; * 101)

    def test_create_with_non_string_types_raises_type_error(self):
        # 계약: 이름, 배역명, 외부 ID가 문자열이 아닐 경우 TypeError가 발생해야 한다.
        with self.assertRaisesRegex(TypeError, &quot;배역명은 문자열이어야 합니다.&quot;):
            ActorVO(name=&quot;타입테스트배우&quot;, role_name=123)
        with self.assertRaisesRegex(TypeError, &quot;외부 ID는 문자열이어야 합니다.&quot;):
            ActorVO(name=&quot;타입테스트배우&quot;, external_id=True)

    def test_optional_fields_can_be_none(self):
        # 계약: 선택적 필드(role_name, external_id)는 None으로 설정될 수 있어야 한다.
        try:
            vo = ActorVO(name=&quot;필수이름만배우&quot;, role_name=None, external_id=None)
            self.assertIsNone(vo.role_name)
            self.assertIsNone(vo.external_id)
        except Exception as e:
            self.fail(f&quot;선택적 필드에 None 할당 시 예외 발생: {e}&quot;)

    def test_actor_equality(self):
        # 계약: 두 ActorVO 객체는 모든 속성 값이 같으면 동등해야 한다.
        vo1 = ActorVO(name=&quot;동일배우&quot;, role_name=&quot;같은역할&quot;, external_id=&quot;ext1&quot;)
        vo2 = ActorVO(name=&quot;동일배우&quot;, role_name=&quot;같은역할&quot;, external_id=&quot;ext1&quot;)
        vo3 = ActorVO(name=&quot;다른배우&quot;, role_name=&quot;같은역할&quot;, external_id=&quot;ext1&quot;)
        vo4 = ActorVO(name=&quot;동일배우&quot;, role_name=&quot;다른역할&quot;, external_id=&quot;ext1&quot;)
        vo5 = ActorVO(name=&quot;동일배우&quot;)
        vo6 = ActorVO(name=&quot;동일배우&quot;)

        self.assertEqual(vo1, vo2)
        self.assertNotEqual(vo1, vo3)
        self.assertNotEqual(vo1, vo4)
        self.assertEqual(vo5, vo6)
        self.assertNotEqual(vo1, vo5)

    def test_actor_hash_consistency(self):
        # 계약: 동등한 ActorVO 객체는 동일한 해시 값을 가져야 한다.
        vo1 = ActorVO(name=&quot;해시값배우&quot;, role_name=&quot;역할1&quot;, external_id=&quot;hash_ext1&quot;)
        vo2 = ActorVO(name=&quot;해시값배우&quot;, role_name=&quot;역할1&quot;, external_id=&quot;hash_ext1&quot;)
        self.assertEqual(hash(vo1), hash(vo2))

    def test_actor_string_representation(self):
        # 계약: ActorVO 객체의 문자열 표현은 적절히 표시되어야 한다.
        vo_name_only = ActorVO(name=&quot;이병헌&quot;)
        self.assertEqual(str(vo_name_only), &quot;이병헌&quot;)

        vo_with_role = ActorVO(name=&quot;마동석&quot;, role_name=&quot;마석도&quot;)
        self.assertEqual(str(vo_with_role), &quot;마동석 (배역: 마석도)&quot;)</code></pre>
<p>나는 위와 같이 설계하다나온 계약, 제약사항을 주석을 통해 명시한다.
내가 ActorVo를 어떻게 구현하든, 어떻게 바꾸든 저 계약을 지켜야한다.
저 테스트는 그걸 보증하는 살아있는 문서다.
만약 요구사항 자체가 바뀌어서 계약이 바뀐다? 그러면 테스트를 수정하면 그만이다. 물론 주석으로 바뀐 계약을 명시한채</p>
<h1 id="assert에-대한-생각">assert에 대한 생각</h1>
<p>처음 테스트 코드를 작성할 때 가장 이해가 안 되는게 바로 assert다.
결과를 만들고 예상하는거랑 맞는지 뭐하러 검사하는 거지? 아무 쓸 때 없는 짓 아닌가?</p>
<p>하지만 시리즈 1, 2, 3에서 보듯 요구사항과 계약이 중요하지</p>
<p>assert의 뜻은 단언하다란 뜻이다. 
왜 단언하는 게 테스트에서 필요하나?
바로 계약이 맞는지 검증해야 하니까</p>
<p><strong>assert(내가 구현한 결과, 계약이 요구하는 결과값)</strong></p>
<p>이 단언은 곧 (내가 구현한 결과 == 계약 만족값)</p>
<p>assert야 말로 테스트의 목적인 계약의 검증을 나타내는 직관적인 표현이다.</p>
<h1 id="결론">결론</h1>
<p>TDD는 객체지향의 조력자고 요구사항과 계약을 지키는지 검사하는 살아있는 문서다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[객체지향 해석(3) - DDD는 객체지향 잘하는 법이다]]></title>
            <link>https://velog.io/@sour_grape/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%ED%95%B4%EC%84%9D3-DDD%EB%8A%94-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%EC%9E%98%ED%95%98%EB%8A%94-%EB%B2%95%EC%9D%B4%EB%8B%A4</link>
            <guid>https://velog.io/@sour_grape/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%ED%95%B4%EC%84%9D3-DDD%EB%8A%94-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%EC%9E%98%ED%95%98%EB%8A%94-%EB%B2%95%EC%9D%B4%EB%8B%A4</guid>
            <pubDate>Sun, 22 Jun 2025 06:53:52 GMT</pubDate>
            <description><![CDATA[<h1 id="객체지향-잘-설계하는-법">객체지향 잘 설계하는 법</h1>
<p>DDD에 대해서 전문적으로 아는건 아니지만 DDD는 단지 객체지향을 잘 짜는 법이라고 생각한다.</p>
<p>잘 설계된 객체지향의 패턴은 무엇인가?</p>
<ol>
<li>적당히 잘 나눠야한다.</li>
<li>적당히 잘 뭉쳐야한다.</li>
<li>뭉친것끼리 잘 메세지를 주고 받아야한다.</li>
<li>메세지를 주고 받을 때 성능, 보안, 안정성등을 확보해야한다.</li>
</ol>
<p>소프트웨어를 설계하는 데 적당히란 워딩이 맘에 안 들수 있다. 하지만 저 <strong>&#39;적당히&#39;</strong>란 말이 설계의 본질이다.</p>
<h1 id="모든-건-트레이드-오프다">모든 건 트레이드 오프다.</h1>
<p>소프트웨어에서 절대적인 솔루션, 단점이 없는 솔루션, 모든걸 해결해주는 솔루션 따윈 없다.</p>
<p>모든 선택은 장단점이 존재하기 때문에 상황을 분석해서 가장 적당한 솔루션을 선택해야 한다.</p>
<p>내가 지어낸 소리가 아니고 아키텍트들이 하는 소리이다.</p>
<p>프로젝트 설계를 할 때 가장 어려운 점이 바로 위와 같은 소프트웨어의 본질 때문이다.</p>
<p>적당히의 기준이 존재하지 않기 때문에 선택은 항상 주관적일 수 밖에 없다.</p>
<p>객체지향을 예를 들면</p>
<ol>
<li>요구사항에 맞게 적당히 비지니스 로직을 나눈다.</li>
<li>요구사항에 맞게 적당히 비지니스 로직을 뭉친다.</li>
<li>요구사항에 맞게 경계끼리 통신을 한다.</li>
<li>요구사항에 맞게 튼튼한 통신을 구현한다.</li>
</ol>
<p><strong>DDD는 이런 &#39;적당히&#39;란 기준을 잡는걸 도와주는 길잡이 역할을 한다.</strong></p>
<h1 id="ddd의-주체">DDD의 주체</h1>
<p>그럼 DDD의 대상이 뭘까? 당연히 내가 전 시리즈에서 말했듣이 <strong>요구사항</strong>이다.</p>
<p>애초에 요구사항을 잘 이끌어내지 못한다면 이런 적당히란 과정을 시작조차 못한다. </p>
<p>따라서 <strong>DDD의 시작은 요구사항을 잘 이끌어내는 것부터 시작한다.</strong></p>
<h1 id="ddd의-시작은-용어집이다">DDD의 시작은 용어집이다.</h1>
<p>다시 소프트웨어는 돈을 버는 행위란걸 명시한다.
돈을 벌기 위해선 고객이나 사용자를 만족시켜야한다.
고객이나 사용자를 만족시킬려면 그들의 요구사항을 만족시켜야한다.</p>
<p>따라서 DDD는 고객과 소통을 통해서 요구사항을 최대한 구체적으로 잡는것부터 시작한다.</p>
<p>우선 고객과 원할한 소통이 되어야 요구사항을 원만하게 이끌어 낼 수 있다.</p>
<p>그래서 DDD는 <strong>유비쿼터스 언어</strong>란걸 정의한다.
대충 말해서 개발자가 고객이 자주 사용하는 용어 위주로 정의하는 것.</p>
<p>이과놈들이랑 이야기 할때 필요도 없는데 전문용어, 영어쓰는 사람들 짜증나지?</p>
<p>평소에도 대화하는데 안 그래도 되는데 전문적인 용어를 쓰거나 일부러 어려운 언어를 사용하는 경우가 있으면 대화하기 싫은것과 마찬가지다.</p>
<p>바로 고객이 느끼는게 그런점이다. 본인이 원하는 요구사항을 이야기하는데 개발자들이 프로그램 언어를 섞어서 쓰면 이해도 안되고 짜증날 수 밖에 없다.</p>
<p>반면 개발자들은 고객이 원하는 요구사항의 배경지식을 충분히 이해해야한다.</p>
<p><strong>도메인</strong>이라고 하는데 비지니스에 대한 이해가 없으면 비지니스의 용어 자체를 이해하지 못한다.</p>
<p><strong>유비쿼터스언어</strong>는 개발자와 고객, 고객끼리의 소통을 원할하기 위해서 비지니스에 사용되는 언어를 정하는 것이다.</p>
<p>정한 용어를 요구사항에 꼭 사용하고 심지어 개발할 때 이용하여 오해의 소지를 없애는 작업이다.</p>
<h1 id="적당히-잘-나누기와-잘-뭉치기">적당히 잘 나누기와 잘 뭉치기</h1>
<p>유비쿼터스 언어를 정의하면서 전체 비지니스안에서 각자의 책임을 가지는 영역을 적당히 나눈다.</p>
<p>이때 사용되는 방법은 이벤트 스토밍이 대표적인데 설명은 안 할거다.</p>
<p>개발자뿐만 아니라 고객들도 직접 참여해서 회의하고 같이 나누는 작업을 하는데 데이터를 기준으로 생각하기도 하고 소유권을 기준으로 생각하기도 한다. 자세한 내용은 DDD와 관련된 교재를 찾아보도록 하자.</p>
<p>비지니스 로직의 경계를 잘 나눴다면 그 경계에 해당되는 것들을 집어넣어서 뭉쳐야한다.</p>
<p>물론 이때도 적당히 뭉쳐야하는게 관건이다. 보통은 트랜잭션을 기준으로 뭉친다. 그리고 뭉친것들중 하나를 대표로 잡아서 출입구 역할을 맡는데 이게 <strong>애그리게이트와 애그리게이트 루트</strong>이다.</p>
<p>통신을 할 때 이런 출입구를 기준으로 통신하기 때문에 객체지향에서
3. 요구사항에 맞게 경계끼리 통신을 한다.
4. 요구사항에 맞게 튼튼한 통신을 구현한다.
을 자연스럽게 가능하게 한다.</p>
<h1 id="결론">결론</h1>
<ul>
<li><p>유비쿼터스 언어는 오해와 추측이 난무하던 소통의 문제를 해결하여 요구사항의 본질을 명확히 했다.</p>
</li>
<li><p>바운디드 컨텍스트는 &#39;책임과 언어&#39;라는 명확한 기준으로 거대한 비즈니스를 &#39;적당히&#39; 나눌 수 있게 해주었다.</p>
</li>
<li><p>애그리게이트는 나뉜 경계 안에서 &#39;데이터 일관성&#39;이라는 원칙하에 관련 개념들을 &#39;적당히&#39; 뭉치는 기준을 제시했다.</p>
</li>
<li><p>애그리게이트 루트는 이렇게 잘 나뉘고 뭉쳐진 책임의 단위들이 서로의 내부를 침범하지 않으면서도, 명확한 &#39;출입구&#39;를 통해 안전하게 소통할 수 있는 길을 열어주었다.</p>
</li>
</ul>
<p>DDD는 이런 방식으로 객체지향을 잘 설계하게 도와주는 방식이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[객체지향 해석(2) - 요구사항과 계약]]></title>
            <link>https://velog.io/@sour_grape/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%ED%95%B4%EC%84%9D2-%EC%9A%94%EA%B5%AC%EC%82%AC%ED%95%AD%EA%B3%BC-%EA%B3%84%EC%95%BD</link>
            <guid>https://velog.io/@sour_grape/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%ED%95%B4%EC%84%9D2-%EC%9A%94%EA%B5%AC%EC%82%AC%ED%95%AD%EA%B3%BC-%EA%B3%84%EC%95%BD</guid>
            <pubDate>Sat, 21 Jun 2025 16:11:32 GMT</pubDate>
            <description><![CDATA[<h1 id="요구사항은-돈이다">요구사항은 돈이다</h1>
<p>개인적으로 소프트웨어에서 중요하다고 생각하는건 돈을 버는 것이라고 생각한다.
사용자든 고객이든 만족시켜서 돈을 버는것이 중요하므로 소프트웨어에서 가장 중요한 건 <strong>요구사항을 이끌어내고 만족시키는 것</strong>이다.</p>
<h1 id="객체지향과-요구사항">객체지향과 요구사항</h1>
<p>요구사항과 객체지향은 아주 근접해있다. 사실 소프트웨어의 거의 모든 것이 요구사항을 잘 만족시키기 위한 것이다.</p>
<p>객체지향이든 TDD든 DDD든 그에 따른 MSA든 모두 요구사항을 명확하게 이끌어내지 못하면 아무 소용이 없다. 반대로 요구사항을 잘 이끌어내고 철저한 분업화에 대해서 이해하면 객체지향과 TDD, DDD 모두 요구사항을 위한 것임을 깨닮게 된다.</p>
<h1 id="요구사항과-계약">요구사항과 계약</h1>
<p>요구사항을 잘 이끌어내는 프로젝트 설계에 대해선 말하지 않겠다. TDD와 DDD 또한 나중에 서술하겠다. 지금은 잘 설계된 현실의 요구사항을 어떻게 소프트웨어로 구현하는지에 대해서만 서술하겠다.</p>
<p>나는 이 시리즈에서 요구사항과 요구사항을 만족시키기 위한 방법, 표현, 약속등을 <strong>계약</strong>이라고 지정하겠다.</p>
<p>잘 설계된 요구사항은 제약과 해야될 행동 등이 정의된다.</p>
<p>예를 들어서 스마트폰의 디스플레이는 다음과 같은 요구사항이 필요하다.</p>
<ol>
<li>전원이 켜진다.</li>
<li>밝기를 조절할 수 있다.</li>
</ol>
<p>이는 요구사항이자, 계약 사항, 제약 조건이므로 우리가 소프트웨어로 이를 구현할 때 <strong>꼭 이뤄야하는 계약</strong>이라고 볼 수 있다.</p>
<p>따라서 이런 계약을 소프트웨어로 표현하고 강제하는 것이 <strong>인터페이스</strong>이다.</p>
<pre><code class="language-java">interface Display {
    void turnOn(); // 전원을 켠다.
    void setBrightness(int level); // 밝기를 조절한다.
    Resolution getResolution(); // 해상도 정보를 제공한다.
}</code></pre>
<p>위의 예를 들면 디스플레이의 계약은 위와 같이 소프트웨어에서 명시 될 수 있다.</p>
<p>*<em>따라서, 이렇게 정의된 계약을 코드로써 보증하고 강제하는 수단이 바로 인터페이스다. 
*</em></p>
<h1 id="개발의-과정">개발의 과정</h1>
<p>운영적인 것을 제외하고 기능구현만을 생각해보면 개발의 과정은 다음과 같다.</p>
<ol>
<li>고객과 회의를 통해 요구사항을 듣는다.</li>
<li>지속적인 회의를 통해 요구사항을 구체화한다.</li>
<li>계약서를 작성한다.</li>
<li>프로젝트 설계를 실행한다.</li>
<li>각 부분에 대한 계약을 코드로 옮긴 인터페이스를 작성한다.</li>
<li>구현 클래스는 인터페이스라는 계약을 반드시 준수하며 코드를 완성한다.</li>
</ol>
<p>프로젝트의 설계가 매우 중요한데 인터페이스를 이용하는 건 이런 설계의 연장선상이라고 볼 수 있다. 따라서 인터페이스는 매우 중요하다.</p>
<h1 id="다시-요구사항과-객체지향">다시 요구사항과 객체지향</h1>
<p>그렇다면 이 모든 것이 객체지향과 무슨 관련이 있는가?</p>
<p>객체지향의 본질이 &#39;분업&#39;이라면, &#39;요구사항&#39;은 바로 그 &#39;분업의 대상&#39;이자 &#39;분업의 기준&#39;이 된다.</p>
<p>우리는 요구사항을 보고 무엇을 나눌지 결정한다. 요구사항을 분석하여 서로 관련 있는 것끼리 뭉친다. 이렇게 요구사항을 기준으로 시스템을 적절하게 나누고 뭉쳐서, 각자에게 명확한 책임을 부여하는 설계 과정 그 자체가 바로 객체지향적 접근법이다.</p>
<p>그리고 이렇게 분업화된 각 객체들이 서로 약속(계약)을 잘 지키도록 강제하는 도구가 바로 인터페이스인 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[객체지향 해석(1) - 분업]]></title>
            <link>https://velog.io/@sour_grape/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%ED%95%B4%EC%84%9D1-%EB%B6%84%EC%97%85</link>
            <guid>https://velog.io/@sour_grape/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%ED%95%B4%EC%84%9D1-%EB%B6%84%EC%97%85</guid>
            <pubDate>Sat, 21 Jun 2025 08:22:56 GMT</pubDate>
            <description><![CDATA[<p>보통 컴퓨터의 역사에서 절차지향과 객체지향이 나온 동기를 이해하며 객체지향을 배운다.</p>
<p>나는 이런 기본이 부족하기 때문에 내 식대로 객체지향을 해석할 것이다.</p>
<h1 id="잡소리">잡소리</h1>
<p>나의 사상은 이렇다. 소프트웨어는 순수하게 사람이 만든 것이기 때문에 소프트웨어의 모든 것엔 의도가 존재한다. </p>
<p>나의 출신이 출신인지라 진리를 탐구한다고 오해하는 물리의 이론이 사람의 주관이 들어간 것이란걸 이해하고 있고, 소프트웨어 또한 마찬가지라고 생각한다.</p>
<p>따라서 소프트웨어를 이해한다는 것은 만든 사람의 선택과 의도를 이해하는 것이다.</p>
<p>만든 사람들이 천재라고 생각하지 말자. 소프트웨어의 생각의 강도는 단단하지 않으므로 쫄지 않고 만든 사람의 의도를 파악하려고 하면 금방 이해할 수 있으니</p>
<h1 id="객체지향의-목적">객체지향의 목적</h1>
<p>객체지향을 왜 만들었을까? 프로그램을 잘 운영하기 위해서이다. 일을 잘하기 위한 모델은 이미 산업화 시대에 만들어졌다.</p>
<p><strong>객체지향은 분업화다.</strong></p>
<p>장인의 시대를 지나 석탄의 발견과 증기기관의 발전, 분업을 통한 공장식 생산이 매우 뛰어난 효율을 가졌다는 것은 자명하다.</p>
<p>싸피를 다니고 있으니 삼성전자의 예를 들어보자.</p>
<p>삼성전자는 철저하게 분업하여 운영된다.</p>
<p>삼성전자 DX, DS, research 등등 그 안에서도 사업부가 있고, 그 사업부 안에선 또 부서가 나뉘었다.</p>
<p>요점은 <strong>각자의 책임을 가지고 철저하게 분업화</strong>가 되어 있다는 점이다.</p>
<p>예를 들어서 삼성이 스마트폰을 만들 때 상품기획, 디자인, 개발, 소프트웨어의 단계를 거치는 데 이때 수 많은 부서가 협력하여 스마트폰을 완성하는 구조이다.</p>
<p>이런 철저한 분업 모델을 그대로 소프트웨어에 적용한 것이 객체지향이라고 할 수 있다.</p>
<p>따라서 객체지향의 원리를 이해하기 위해선 우선 객체지향이 철저한 <strong>분업화</strong>란 것을 명심해야한다.</p>
<h1 id="디자인-패턴-객체지향의-법칙">디자인 패턴, 객체지향의 법칙?</h1>
<p>나는 이런거 안 외운다.</p>
<p>내가 소프트웨어를 공부하며 느끼는 게 소프트웨어 분야는 이상하게 용어와 정의에 집착하는 경향이 있는 것 같다.</p>
<p>캡슐화, 다형성, 추상화 등등 이런거에 대해서 설명해보세요란 문제들이 있고 이를 외우는 건 별로 도움이 되는 것 같지 않다.</p>
<p>분업화의 관점에서 보면 그냥 저절로 이해가 되는 원칙들이 많다.
다시 삼성의 예를 들어보자.</p>
<p>삼성이 철저한 분업화로 최적의 효율을 추구했는데, 분업화를 망치면 효율이 떨어진다.</p>
<p>스마트폰의 디자인은 디자인 부서가, 디스플레이를 만드는 건 디스플레이 부서, 회사의 운영은 경영 부서가 맡아야지 다른 부서가 책임을 맡을 필요도 없고 속사정을 알아서도 안된다.<strong>(캡슐화)</strong></p>
<p>이런 부서들이 협력할 땐 그냥 메세지만 보내면 된다. 스마트폰을 조립하는 애들은 각각의 재료들을 담당하는 부서에 그냥 재료내놔 이 한마디만 하고 결과물로 재료만 받아야 한다.<strong>(메세지)</strong></p>
<p>이런 메세지를 주고 받을 땐 각각의 부서의 자세한 구현을 알 필요는 없다.</p>
<p>디스플레이 내놔(o)</p>
<p>너네 공장의 시리얼 넘버(xxs<del>), 언제 생산되었고 재료는 ~</del>, 생산 공장은 ~~인 디스플레이 내놔 (x)</p>
<p><strong>(추상화)</strong></p>
<p>철저하게 분업화를 생각하면 그것이 객체지향이다.</p>
<h1 id="안티-패턴">안티 패턴</h1>
<p>철저하게 분업화가 안되면 그게 객체지향의 안티 패턴이다.</p>
<p>블라인드에서 거론되는 삼성의 문제점이 대표적이 예다.</p>
<p>경영하는 애들은 기술에 대해서 알 필요가 없다. 기술은 온전히 기술자의 책임이어야 하고 경영은 경영의 책임만 다하면 된다.</p>
<p>경영이 기술의 영역을 침범하여 관여하는 건 분업화에 실패한 것이고 효율을 망치는 일이다.</p>
<p>객체지향도 마찬가지다. 객체지향에서 거론되는 모든 문제는 한 객체가 다른 객체를 침범하고 너무 깊게 연관되어서 발생한다.</p>
<p>적절하게 뭉치고, 적절하게 책임을 나누어서 철저하게 분업화 하는게 객체지향을 잘 설계하는 방법이라고 생각한다.</p>
<p>다음 시간엔 이런 방법에 대해서 논의하겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[음악 분류 딥러닝을 만들자(42) - Bayesian, gaussian, VAE 등을 위한 사전 준비]]></title>
            <link>https://velog.io/@sour_grape/%EC%9D%8C%EC%95%85-%EB%B6%84%EB%A5%98-%EB%94%A5%EB%9F%AC%EB%8B%9D%EC%9D%84-%EB%A7%8C%EB%93%A4%EC%9E%9042-Bayesian-gaussian-VAE-%EB%93%B1%EC%9D%84-%EC%9C%84%ED%95%9C-%EC%82%AC%EC%A0%84-%EC%A4%80%EB%B9%84</link>
            <guid>https://velog.io/@sour_grape/%EC%9D%8C%EC%95%85-%EB%B6%84%EB%A5%98-%EB%94%A5%EB%9F%AC%EB%8B%9D%EC%9D%84-%EB%A7%8C%EB%93%A4%EC%9E%9042-Bayesian-gaussian-VAE-%EB%93%B1%EC%9D%84-%EC%9C%84%ED%95%9C-%EC%82%AC%EC%A0%84-%EC%A4%80%EB%B9%84</guid>
            <pubDate>Tue, 28 Jan 2025 12:10:20 GMT</pubDate>
            <description><![CDATA[<h1 id="사전-준비를-위한-교재와-논문-지식">사전 준비를 위한 교재와 논문, 지식</h1>
<p>이제 적용할 기법은 bayesian, guassian, MCMC, VAE, Diffusion등 확률론과 관련된 기법들을 적용할 것이다.
이 모든 기법은 베이지안 통계학을 기초로 하고 있어서 베이지안에 대한 내용을 학습해야한다.</p>
<p>이미 2020년 정도에 BANANAS란 경량화 기법이 나왔고 앞선 시리즈에서 소개하듯 predictor와 bayesian optimization을 결합한 모델이다. 2024년에 DiffusionNag의 획기적인 논문이 나와 앞으로 트렌드를 이끌어갈꺼라고 생각한다. diffusion을 이용한 neural architecture generation이란 기법을 위해서 사전 지식이 필요한데 나는 다음과 같은 자료들로 입문했다.</p>
<h1 id="머피책---advanced">머피책 - advanced</h1>
<p>케빈 머피의 확률론적 머신러닝의 심화편을 읽자. 확률론적으로 딥러닝을 생각하는 입문서로 매우 적절한 책이다. 또한 일련의 흐름들로 기초적인 모델부터 현재 유행하는 모델인 diffusion, 적대적 학습까지 모두 배울 수 있다.
내용이 깊다고 생각할 수 있지만 생각보다 수준이 쉽다. 개론 정도의 수준이지만 양이 많아서 문제지</p>
<p>2달 정도 2-3 시간씩 매일은 아니고 일주일에 2-3일정도 읽었다. 시간 투자할만한 책이다.</p>
<h1 id="파이썬을-활용한-베이지안-통계">파이썬을 활용한 베이지안 통계</h1>
<p>별로 좋은 책은 아니다. 일단 책에서 저자가 직접 만든 라이브러리를 사용하는데 솔직히 좀 그렇다. 베이지안의 기본적인 지식을 익힐 수 있고 그냥 실습할 수 있는 거의 유일한 책이란 점에서 한 번 읽을만 하다. </p>
<p>한 시간씩해서 일주일 정도 걸림</p>
<h1 id="밑바닥부터-시작하는-딥러닝-5">밑바닥부터 시작하는 딥러닝 5</h1>
<p>디퓨전을 위해서 필요한 지식과 구현을 제공한다. 
현재 시점에선 교재론 다른 대안이 없어서 좋던 싫던 봐야할 책</p>
<p>그래도 논문보고 논문 저자의 구현체를 보며 삽질하는 것보단 훨씬 좋다.
이 시리즈가 유명하고 친절해서 어느정도 신뢰성은 보장한다. 내용도 괜찮은 편이니 읽도록 하자.</p>
<h1 id="논문들">논문들</h1>
<p>BANANAS, DiffusionNag란 두 논문을 기본으로 필요한 논문들을 찾아 읽도록하자.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[13기 싸피 인터뷰에서 썰 풀다 합격한 썰 푼다]]></title>
            <link>https://velog.io/@sour_grape/%EC%8B%B8%ED%94%BC-%EC%9D%B8%ED%84%B0%EB%B7%B0%EC%97%90%EC%84%9C-%EC%8D%B0-%ED%92%80%EB%8B%A4-%ED%95%A9%EA%B2%A9%ED%95%9C-%EC%8D%B0-%ED%91%BC%EB%8B%A4</link>
            <guid>https://velog.io/@sour_grape/%EC%8B%B8%ED%94%BC-%EC%9D%B8%ED%84%B0%EB%B7%B0%EC%97%90%EC%84%9C-%EC%8D%B0-%ED%92%80%EB%8B%A4-%ED%95%A9%EA%B2%A9%ED%95%9C-%EC%8D%B0-%ED%91%BC%EB%8B%A4</guid>
            <pubDate>Thu, 19 Dec 2024 17:27:49 GMT</pubDate>
            <description><![CDATA[<img src="https://velog.velcdn.com/images/sour_grape/post/fc49b7b2-bbd6-4b83-a99d-a12d57a6efe2/image.png" width="500"/>

<h1 id="introduction">introduction</h1>
<p>이래저래 벨로그 감성으로 썰 푸는건 못해서 글이 건조할 것 같다
내가 나름대로 세운 전략이 정확히 들어 맞았고 면접을 찢었기(?) 때문에 내 전략이 다음 기수 지원자들에게 도움이 됐으면 하는 바람으로 글을 적는다</p>
<p>참고로 <strong>비전공자</strong>다</p>
<h1 id="상황-이해">상황 이해</h1>
<p>전략을 세우기 전에 상황을 정확히 인지하는게 중요하다</p>
<p>우리의 목표는 싸피에 합격하는 것이기 때문에 <strong>합격을 목표</strong>로 삼고 각 과정을 분석하며 전략을 세우자.</p>
<h2 id="높은-경쟁률">높은 경쟁률</h2>
<p>오피셜은 없지만 대략 10:1 내외의 경쟁률로 추정되고 있다</p>
<h2 id="평가-지표">평가 지표</h2>
<p>비전공자의 평가 지표는 다음과 같다</p>
<ol>
<li><p>1차 평가는 ct, 에세이</p>
</li>
<li><p>2차 평가는 pt, 인성 면접</p>
</li>
</ol>
<h2 id="1차-관문">1차 관문</h2>
<p>1차 시험의 목적은 <strong>지원자 거르기</strong>다</p>
<p>대한민국의 모든 입사 시험이 그렇듯 <strong>1차 시험</strong>은 높은 경쟁률에서 기업 입장에서 <strong>최대한 간편하고 효율적</strong>으로 지원자를 거르는 과정이다.</p>
<p>1차 평가는 지원자들이 싸피 교육에 맞는 인재인지 아닌지를 평가하는 과정이 아니고 그냥 <strong>속아내는 과정</strong></p>
<h2 id="2차-관문">2차 관문</h2>
<p>1차 시험으로 걸러낸 지원자들을 평가하는 과정으로 크게 pt면접과 인성 면접으로 이루어진다</p>
<p>후술하겠지만 1차 시험을 통해 지원자들을 속아내긴 했어도 여전히 많기 때문에 <strong>면접관들은 지원자들의 서류와 에세이에 미리 시간을 투자하는게 불가능하다</strong></p>
<h1 id="전략">전략</h1>
<h2 id="두괄식-키워드">두괄식 키워드</h2>
<p>에세이, 1분 자기소개, pt, 인성면접 등등 <strong>ct문제를 제외</strong>한 모든 건 <strong>두괄식 키워드</strong>로 작성하도록 한다
주절주절 쓰지말고 최대한 키워드 위주로 깔끔하게 쓴다</p>
<h2 id="키워드와-진솔한-경험">키워드와 진솔한 경험</h2>
<p>키워드를 중심으로 모든 게 강제되기 때문에 모든 키워드는 <strong>진솔한 경험</strong>에서 나와야 한다</p>
<h1 id="에세이">에세이</h1>
<p><img src="https://velog.velcdn.com/images/sour_grape/post/af2e36fa-7c6e-45c3-90d9-59754c491adc/image.png" alt=""></p>
<p>2차 면접의 경쟁률을 대충 2:1정도로 잡아보자. 그리고 총 경쟁률을 10:1로 치고 싸피 합격자를 1100명 정도로 생각해보자. 그러면 면접관이 읽어야되는 총 에세이는 <strong>11000개</strong>, 그 중 <strong>2200개</strong> 정도만 선발을 해야한다.</p>
<p>면접관의 수를 100명으로 잡아도 읽어야하는 에세이만 110개 정도인 상황에서 지원자의 상황은 웹소설과 비슷하다고 생각한다.</p>
<h2 id="어그로">어그로</h2>
<p>웹소설, 웹툰, 만화 같은 컨텐츠는 경쟁작이 너무 많기 때문에 일단 클릭을 해서 읽게 하는게 중요하다</p>
<p>그래서 문장형 어그로 제목이 유행하고 있다</p>
<p>&#39;주인공이 힘을 숨김&#39;, &#39;데뷔 못 하면 죽는 병 걸림&#39;, &#39;괴담에 떨어져도 출근은 해야 하는구나&#39;와 같이 제목만 보고도 소설의 내용이 예상이 가면서 궁금증을 유발해 클릭할 수 밖에 없는 구조이다</p>
<p>각각을 살펴보면</p>
<ol>
<li><p>키워드: 주인공, 힘, 숨김</p>
</li>
<li><p>키워드: 데뷔 못 하면, 죽는 병</p>
</li>
<li><p>키워드: 괴담, 떨어져도, 출근</p>
</li>
</ol>
<p>와 같이 키워드 중심인걸 알 수 있는데 <strong>우리의 에세이도 키워드를 중심으로 어그로를 끌어 서류 심사관의 집중력을 높여야 한다</strong></p>
<p>13기의 에세이 주제는 다음과 같다.</p>
<blockquote>
<p>학업 및 취업준비를 하며 가장 어려웠던 경험과 이를 해결하기 위해 했던 노력을 기술하고, SSAFY에 지원하신 동기에 대해서도 작성 바랍니다. (500자 내외)</p>
</blockquote>
<p>면접관이 최대한 읽기 편하게, 그러면서도 지원자에게 관심이 생기게 주절주절 없이 최대한 깔끔하게 작성하자</p>
<p>또한 뒤에서 이야기 하겠지만 인터뷰에선 면접관이 1분 안에 그 자리에서 처음보는 에세이에서 키워드를 추출해야 되기 때문에 두괄식 키워드로 쓴다. </p>
<p>500자란 에세이에 2가지 주제의 글을 써야 하는데 당연히 500자론 날 다 표현하지 못한다.</p>
<p>따라서 위와 같이 문장형 어그로 제목처럼 짧은 글 안에 두괄식 키워드를 숫자와 같은 지표를 활용해서 적자</p>
<h2 id="내-에세이-키워드">내 에세이 키워드</h2>
<p>일단 에세이 키워드는 정말 진솔한 경험들에서 가져왔다.</p>
<p>어려웠던 경험 키워드: 최신 경량화 논문, 음악 분류 딥러닝 프로젝트, 최신 논문, Neural Architecture Search(Nas), 6개월 동안 100편의 논문, 10여 편을 직접 구현 등등</p>
<p>SSAFY 지원 동기: 협업의 중요성, 취업 지원 등등</p>
<p>내 에세이는 저 키워드를 두괄식으로 정말 간결하게 이루어져 있다</p>
<p>글 내용도 500자가 안돼서 억지로 불려놨다</p>
<p>500자가 겨우 되는 글에 <strong>키워드만 10개정도</strong> 된다</p>
<p><strong>키워드를 구체적인 수치로 제시해놨고 자세한 상황이나 이유는 없고 보충할 건 간단하게 1줄정도로 붙여줌</strong></p>
<p>내가 6개월 동안 고민하고 있는 딥러닝 프로젝트로 키워드를 뽑아냈기 때문에 투자한 시간은 20분 내외, gpt한테 두괄식으로 수정하고 맞춤법 검사기만 돌리고 바로 제출했다</p>
<h1 id="싸피-면접-상황">싸피 면접 상황</h1>
<h2 id="면접의-3대-요소">면접의 3대 요소</h2>
<p>어떤 면접이든 1분 자기소개는 당연하다. 따라서 싸피의 인터뷰는 3개의 요소로 이루어져 있다.</p>
<ol>
<li><p>1분 자기소개</p>
</li>
<li><p>pt 발표</p>
</li>
<li><p>인성 면접(에세이+ 1분 자기소개)</p>
</li>
</ol>
<h2 id="면접관의-상황을-분석-해보자">면접관의 상황을 분석 해보자</h2>
<p>이건 순전히 내 추측이지만 면접관들이 한 시간에 2~3명의 지원자들을 본다고 하고 하루에 총 면접시간은 8시간으로 잡자</p>
<p>그러면 면접관들이 <strong>하루에 16~24명 정도의 지원자</strong>들의 면접을 진행 한다는 뜻이고 면접관이 5일중 3일만 참여한다고 해도 대충 50, 60명의 지원자들을 봐야한다는 뜻이다.</p>
<p>따라서 면접관들이 지원자의** 에세이를 사전에 파악한다는 건 불가능**하다.</p>
<p>또한 나머지 3대 요소인 1분 자기소개와 pt발표 역시 스크립트가 미리 면접관에게 주어지는 게 아니라 <strong>발표를 진행하며 실시간으로 듣는다</strong></p>
<h2 id="면접관의-상황--내-전략의-방향성">면접관의 상황 == 내 전략의 방향성</h2>
<blockquote>
<p>면접관들은 1분 자기소개 그 자리에서 듣고, 에세이도 빠르게 1분 정도만 읽고, pt발표도 스크립트 없이 듣기만 한다</p>
</blockquote>
<p><strong>면접관들에게 인터뷰는 듣기 평가와 다를바가 없고 모든 순간에 빠르게 지원자 발표의 키워드만 기록해야한다.</strong></p>
<h3 id="1-주절주절-장황하면-안되는-이유">1. 주절주절, 장황하면 안되는 이유</h3>
<p>여러분이 8시간 동안 듣기 시험만 친다고 하자. 정말 정신적으로 피곤하고 그 수 많은 말 속에서 <strong>키워드만 추출</strong>해야 되기 때문에 </p>
<blockquote>
<p>주절주절 == 짜증</p>
</blockquote>
<h3 id="2-두괄식-키워드를-써야되는-이유">2. 두괄식 키워드를 써야되는 이유</h3>
<p>면접관들은 지원자의 글과 발표의 디테일엔 관심이 없고 키워드에 관심이 있다.</p>
<p>그럼 두괄식으로 키워드를 깔끔하게 뇌에 박아주면 편해서 존나 좋아함</p>
<h1 id="1분-자기소개">1분 자기소개</h1>
<p><strong>1분 자기소개는 면접의 첫 인상을 좌우하고 인성 면접의 토대가 되기 때문에 어떤 상황에서든 튀어나오게 외우자</strong></p>
<p><img src="https://velog.velcdn.com/images/sour_grape/post/7ecf670e-d3e9-4c1a-9af4-776e85ced330/image.png" alt=""></p>
<p><a href="https://www.youtube.com/watch?v=x5RBA8RLdt0&amp;ab_channel=%EB%A9%B4%EC%A0%91%EC%99%95%EC%9D%B4%ED%98%95">https://www.youtube.com/watch?v=x5RBA8RLdt0&amp;ab_channel=%EB%A9%B4%EC%A0%91%EC%99%95%EC%9D%B4%ED%98%95</a></p>
<p><a href="https://www.youtube.com/watch?v=JY69uSxtDzA&amp;ab_channel=%EB%A9%B4%EC%A0%91%EC%99%95%EC%9D%B4%ED%98%95">https://www.youtube.com/watch?v=JY69uSxtDzA&amp;ab_channel=%EB%A9%B4%EC%A0%91%EC%99%95%EC%9D%B4%ED%98%95</a></p>
<p>내 모든 인터뷰 전략은 면접왕 이형의 이 영상에서 그대로 가져왔다</p>
<p>요약하면 <strong>면접관들이 질문을 내 키워드로 강제시키는 것이다</strong></p>
<p>1분 자기소개도 이 영상을 바탕으로 다음과 같이 만들었다.</p>
<h2 id="예시">예시</h2>
<p><strong>인사말 1줄</strong></p>
<p><strong>필살기1 how+result 2-3줄</strong>
<strong>입교후 포부 2줄</strong></p>
<p>안녕하세요 이용자의 관점을 생각하는 <del>~</del>입니다.</p>
<p>딥러닝에 관심이 있어 프로젝트를 진행하던 중 학습시간과 비용의 이용자 대부분이 겪는 문제란 것을 경험하였습니다. 
문제점을 극복하기 위해 딥러닝 경량화 프로젝트를 진행하며 개발자에 대해 관심을 갖게 되었습니다.</p>
<p>제가 싸피에 입과하게 된다면  mlops와 웹 백엔드의 역량을 쌓고
부족한 알고리즘의 역량을 쌓아 빠르게 it 업무에 뛰어들고 싶습니다.</p>
<p>1분 자기소개는 두괄식이 아닌 평범한 형태로 했다. 하지만 키워드를 위주로 던졌다.</p>
<p><strong>키워드</strong>: 딥러닝, 학습시간과 비용, 경량화 프로젝트, mlops, 웹 백엔드, 알고리즘</p>
<p><strong>6개를 대놓고 던져서 이거 물어보세요란 전략이다</strong></p>
<h1 id="pt-면접">pt 면접</h1>
<p>스터디 안하고 혼자 준비했다</p>
<p>일주일 정도 pt스크립트 작성과 it기사를 읽는데 하루에 2시간 정도 투자한 것 같다</p>
<p><img src="https://velog.velcdn.com/images/sour_grape/post/a5e55ee3-544b-4104-a926-53da76e36f94/image.png" alt=""></p>
<p><a href="https://www.youtube.com/watch?v=DOvCIrwMPbQ&amp;ab_channel=%EA%B0%95%EB%AF%BC%ED%98%81">https://www.youtube.com/watch?v=DOvCIrwMPbQ&amp;ab_channel=%EA%B0%95%EB%AF%BC%ED%98%81</a></p>
<h2 id="스크립트">스크립트</h2>
<p>싸피 합격 후기에 누구나 따라하는 위 동영상을 보고 그대로 준비했다.
면접가면 다 이 방법으로 준비할 것 같지만 내 시야에 종이를 접는 사람은 아무도 없었다.</p>
<p>동영상 보면 종이 접어서 4가지 영역으로 pt를 끼워 맞춘다.
나는 이렇게 4가지로 했다.</p>
<ol>
<li><p>문제 정의</p>
</li>
<li><p>해결 방법</p>
</li>
<li><p>risk</p>
</li>
<li><p>극복 방안</p>
</li>
</ol>
<p>스터디 없이 혼자 했기 때문에 <strong>gpt</strong>를 이용했다.</p>
<h2 id="pt-스크립트-연습">pt 스크립트 연습</h2>
<blockquote>
<p>it pt 면접에 알맞은 주제를 제시해줘 키워드와 함께 미래 it 신기술 위주로 제시해줬으면 좋겠어 주제별로 좀 더 구체적으로 문제 해결형이랑 의견 제시형으로 </p>
</blockquote>
<p>이런식으로 gpt에게 주제를 주라고 하면 </p>
<p>클라우드 기반 데이터 보안 문제</p>
<p>문제 정의: 다중 사용자 환경에서 데이터 보호 및 프라이버시 문제가 부각되고 있다.
키워드: 데이터 암호화, 제로 트러스트 보안 모델, 클라우드 접근 제어.
해결 방향: 클라우드 네이티브 보안 솔루션 도입 및 보안 표준 강화.</p>
<p>이런식으로 준다.</p>
<p>그러면 종이에 다음과 같이 써서 연습했다.</p>
<h3 id="수정전">수정전</h3>
<p>클라우드 기반 데이터 보안 문제 
문제 정의: 클라우드 서비스의 개인 데이터 유출, 정보의 무단 사용이 문제가 되고 있다. 특히 이용 회사의 데이터 유출시 막대한 피해가 발생해 클라우드 서비스에 신뢰성에 문제가 제시되고 있다.
해결: 첫째, 클라우드 보안 정책을 확립한다. 보안 표준을 만들고 지속적으로 강화한다. 클라우드 등급별 접근 제어를 실시해 보안을 강화한다.
둘째,  규정준수를 자동화한다. 규정을 준수하도록 강제화하고 벗어날시 경고하여 준수하도록 인도한다.
risk: 첫 째, 정책 확립 중 클라우드 회사의 이익과 소비자의 정보 보호가 상충될 수 있다.
둘 째, 범죄와 관련된 데이터의 검열이 논란을 일으킬 수 있다.
극복: 정책을 소비자에게 투명하게 공개해 동의를 얻는다. 위치 데이터 등 데이터의 활용을 사전에 고지한다.
둘 째, 단계적 검열을 도입한다. 상시 정부에 데이터를 제공하거나 검열하는 것이 아닌 단계적 규정에 따라 검열을 검토한다.</p>
<h3 id="수정후">수정후</h3>
<p>gpt에게 수정해달고 하면 주로 받았던 피드백은 구체적인 키워드를 제시하란 거였다.</p>
<p>문제 정의
클라우드 보안 위협: 개인 정보 유출, 데이터 무단 사용.
사례: 대규모 기업 데이터 유출 사건 (피해 규모: 수억 달러).
결과: 클라우드 서비스 신뢰성 저하, 사용자 및 기업에 심각한 위협.</p>
<p>해결 방안</p>
<p><strong>제로 트러스트 아키텍처</strong> 도입
<strong>데이터 등급별 접근 제어</strong>
<strong>규정 준수 자동화</strong></p>
<p>Risk
이익 충돌: 기업 수익성과 데이터 보호 요구 간의 상충 가능성.
윤리적 논란: 범죄 데이터 검열 문제.</p>
<p>극복 방안
정책 투명성 강화</p>
<p>데이터 사용 내역 공개.
소비자 동의 절차 확립.
단계적 검열 도입
긴급 상황 시 독립적 검토 후 제한적 검열 시행.</p>
<p>위와 같이 수정된 발표 스크립트에 맞춰서 구체적인 키워드 위주로 작성하였다</p>
<h2 id="발표-source">발표 source</h2>
<p>gpt와 종이에 직접 스크립트를 짠 연습을 한 뒤에는 발표 source를 읽었다. 내가 선택 한건 <strong>삼성 sds 인사이트 리포트</strong>다.</p>
<p><a href="https://www.samsungsds.com/kr/insights/index.html">https://www.samsungsds.com/kr/insights/index.html</a></p>
<p>삼성 sds 인사이트 리포트는 최신 it 주제들을 pt 발표 형식으로 문제 제시와 해결 방안등을 소개해주기 때문에 매우 좋다.</p>
<p>1년치 뉴스를 읽었다 아주 도움되니 읽도록 하자</p>
<p>인사이트를 읽고 여러번 gpt나 스터디로 피드백을 받으면 대부분의 신기술은 연결되어 있고 특정한 키워드를 이용할 수 있다는걸 알게된다.</p>
<p>예를 들어서 빅데이터, 클라우드, ai, 엣지 컴퓨팅등은 한꺼번에 엮일 수 있다. 그리고 해결법이나 극복 방안의 구체적인 키워드도 정해져있다. 예를 들어 설명해보면</p>
<ol>
<li><p><strong>ai, 빅데이터들은 처리시간</strong>이 김. 하지만 응급 상황이나 로봇, iot 등은 빠른 처리 시간이 필요하다 -&gt; <strong>엣지 컴퓨팅</strong>을 이용한다.</p>
</li>
<li><p><strong>ai는 클라우드</strong>를 통해서 데이터를 저장하는데 보안문제가 risk임 -&gt; <strong>제로 트러스트 전략</strong>을 이용한다, <strong>엣지 컴퓨팅</strong>을 이용해 데이터를 중요도 별로 보안 처리한다.</p>
</li>
<li><p><strong>ai는 대규모 데이터 센터가 필요</strong>해서 환경문제가 심각하다 -&gt; <strong>모델 경량화, 데이터 센터에서 발생하는 열의 재활용을 이용</strong>한다.</p>
</li>
</ol>
<p>이런식으로 pt가 숙달되면 신기술별로 구체적인 키워드를 알 수 있는데 <strong>pt발표에 꼭 제로 트러스트, 엣지 컴퓨팅 등등 구체적인 키워드를 제시하도록 한다</strong></p>
<p>그럼 당연히 pt발표에선 구체적인 키워드가 뭔지 물어보겠죠?
비전공자가 자세하게 키워드를 제시하고 개념이랑 왜 이 키워드를 pt문제에 적용했는지 설명하면 걍 합격임</p>
<h2 id="중요한-점">중요한 점</h2>
<ol>
<li><p>pt발표를 위와 같은 방식으로 종이를 접어 준비 할 경우 연습이 중요하다. pt준비에 주는 시간은 종이를 접어서 위 형식으로 작성한 뒤 한 번 발표 연습을 하면 딱 맞는 시간을 주니 숙달해서 가자.</p>
</li>
<li><p><strong>키워드를 위주로 발표</strong>하자. 대신 pt발표에서 면접관은 무조건 키워드를 물어보기 때문에 완벽하게 알고가자</p>
</li>
</ol>
<h1 id="인성-면접">인성 면접</h1>
<p>대부분 아래와 같은 인성 면접을 준비하곤 한다.</p>
<blockquote>
<p>본인의 특기가 뭔가?
실패했던 경험?
본인의 성격을 솔직하게 말해보세요
앞으로 어떤 개발자가 되고 싶나?
싸피에 지원한 이유
팔로형인가 리더형인가
팀원 간의 불화가 있을 때 어떻게 대처할 것인가?
본인의 전공을 선택한 계기 or 개발자가 되고 싶은 이유
백엔드를 선택한 이유
본인의 장단점
어떤 취미가 있나
뽑아야 하는 이유
최근에 읽은 IT 기사를 소개해주세요
마지막으로 궁금한점</p>
</blockquote>
<p>내 전략은 내가 준 키워드에서 질문하도록 유도하는 것이라 위와 같은 인성 면접은 하나도 준비 안했고 <strong>실제로 저렇게 추상적인 질문은 하나도 안 나왔다</strong></p>
<p>면까몰이라 어떤 면접관들은 저런 질문할 수도 있겠지만 키워드를 <strong>내가 두괄식으로 뇌에 박아줬는데 과연 그럴까?</strong></p>
<h1 id="면접-후기">면접 후기</h1>
<p><img src="https://velog.velcdn.com/images/sour_grape/post/8bfc46bb-5579-4b0a-a3df-c3d8d105ad67/image.jpg" alt=""></p>
<h2 id="내가-던진-키워드">내가 던진 키워드</h2>
<ol>
<li><p>1분 자기소개: 딥러닝, 학습시간과 비용, 경량화 프로젝트, mlops, 웹 백엔드, 알고리즘</p>
</li>
<li><p>pt 면접: 대외비라 말 못하는데 대략 문제 해결-risk-극복 방안의 큰 흐름이 2개 정도 있었고 각 흐름마다 매우 구체적인 키워드 3, 4개를 제시해서 8개 정도</p>
</li>
<li><p>에세이: 최신 경량화 논문, 음악 분류 딥러닝 프로젝트, 최신 논문, Neural Architecture Search(Nas), 6개월 동안 100편의 논문, 10여 편을 직접 구현, 협업의 중요성, 취업 지원</p>
</li>
</ol>
<h2 id="면접-과정">면접 과정</h2>
<p>내가 면접관들의 뇌에 박은 키워드만 대충 20개다.</p>
<p>*<em>당연히 면접관들도 질문할 게 넘쳤고 비전공자 치곤 키워드가 특이했기 때문에 압박 면접이 아니라 진짜 궁금해서 물어보는 질문들이였다.
*</em></p>
<h3 id="pt-면접-1">pt 면접</h3>
<p>매우 구체적인 키워드 위주로 박아놨기 때문에 내가 의도한 키워드에서만 질문 나왔고, 질문이 끊임 없었고, 모든 질문에 답했으며, 질문 시간을 넘겨서 중간에 끊겼다.</p>
<h3 id="인성-면접-1">인성 면접</h3>
<p>내가 준 키워드</p>
<p>딥러닝, 학습시간과 비용, 경량화 프로젝트, mlops, 웹 백엔드, 알고리즘, 딥러닝 모델 경량화, 음악 분류 딥러닝 프로젝트, 최신 논문, Neural Architecture Search(Nas), 6개월 동안 100편의 논문 읽기, 10여 편을 직접 구현, 협업의 중요성, 취업 지원, 통계 물리학 연구소 인턴, 졸업 논문 등등</p>
<p>*<em>모든 인성 면접 질문은 위 키워드를 1, 2, 3개씩 조합해서 정말 구체적으로 꼬리 질문이 들어왔다. 
*</em></p>
<p>질문은 압박 질문이 아닌 키워드를 보고 그 자리에서 즉석으로 정말 궁금한 것 위주로 들어왔다.</p>
<p>예를 들어서 실제 질문은 아니지만 어려움 극복경험, 협업, 싸피의 필요성 등등 지원자들이 많이들 준비하는것들을 내 키워드와 연관해서 *<em>정말 구체적으로 물어보는 식이였다 *</em></p>
<p><strong>모든 키워드는 내 진솔한 경험에서 나왔기 때문에 나는 그냥 썰만 풀면 됐다.</strong></p>
<p>예를 들어서 실제 면접관이 한 질문은 아니지만 논문이나 경량화 프로젝트에 연관된 질문이 들어오면 6개월 동안 삽질을 했기 때문에 어려움 극복이든, 협업이든, 알고리즘이든 뭐든 내 썰만 풀면 됐다.</p>
<p><strong>인성 면접 역시 질문은 매우 많았지만 모든 질문은 내 키워드에서 나와 내가 경험한 일만 말하면 되니 긴장감은 없었으며 역시나 면접관들에게 내 썰만 재밌게 풀다가 시간을 넘겨서 종료되었다.</strong></p>
<p>그리고 최종 합격함</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[kevin p murphy의 probabilistic machine learning advanced topics 리뷰]]></title>
            <link>https://velog.io/@sour_grape/kevin-p-murphy%EC%9D%98-probabilistic-machine-learning-advanced-topics-%EB%A6%AC%EB%B7%B0</link>
            <guid>https://velog.io/@sour_grape/kevin-p-murphy%EC%9D%98-probabilistic-machine-learning-advanced-topics-%EB%A6%AC%EB%B7%B0</guid>
            <pubDate>Fri, 13 Dec 2024 13:27:30 GMT</pubDate>
            <description><![CDATA[<h1 id="책-링크">책 링크</h1>
<p><a href="https://probml.github.io/pml-book/">https://probml.github.io/pml-book/</a></p>
<p>케빈 머피의 책은 크게 2가지 버전이 있다.</p>
<p>“Probabilistic Machine Learning: An Introduction”과 “Probabilistic Machine Learning: Advanced Topics”</p>
<h1 id="기본편">기본편</h1>
<p><img src="https://velog.velcdn.com/images/sour_grape/post/ab62dc61-f61f-43e9-9bb7-43593c555035/image.png" alt=""></p>
<p>기본편은 번역본이 존재한다. 안 읽어봤는데 번역이 좋다고 하니까 살 사람은 사서 보도록
하지만 가격이 미쳤다. 72000원? 미쳤나? 근데 원서가격은 미국에서도 10만원이 넘어서
괜찮나?는 개뿔
난 비싸서 살 계획이 없다.</p>
<p>기본편은 진짜 기본적인 내용을 담고 있다. <strong>내가 초보자면 케빈 머피 책은 안 보는게 좋긴하다.</strong>
읽어도 별로 와닫지 않을건데</p>
<h2 id="기본편-가이드라인">기본편 가이드라인</h2>
<ol>
<li><p>ai에 관심이 생겨서 이제 시작해볼려고 한다. 하지만 이론적인걸 모른다. (이공계 학부 신입생, 비이공계 출신 개발자 등등) -&gt; <strong>미적분학, 미분 방정식, 선형대수학, 기초 통계학을 공부</strong>하고 이 책은 나중에 볼 것</p>
</li>
<li><p>나는 이공계 학부를 졸업했다. 공부하면서 미적분학, 선형대수학 등 공업수학을 공부했고 통계학도 어느정도 안다. 하지만 딥러닝 경험은 없다.-&gt; 밑바닥부터 시작하는 딥러닝을 공부하며 클론코딩 후 관심있는 분야의 프로젝트를 할 것</p>
</li>
<li><p>이론을 실제로 적용해봤고 프로젝트도 1, 2개 해봤지만 <strong>지식의 정리</strong>가 필요하다.</p>
</li>
</ol>
<p>-&gt; 머피책 기본편을 볼 것</p>
<p>사실 난 기본편은 안 봐서 자세한 리뷰는 못한다. 하지만 목차를 보면 그래도 프로젝트 경험 1, 2개는 있는 상태가 적합하다고 생각한다.</p>
<h1 id="심화편">심화편</h1>
<p>*<em>probabilistic machine learning advanced topics *</em></p>
<p>보통 심도있는 머신러닝 이론 책을 추천해주라고 하면 </p>
<p><img src="https://velog.velcdn.com/images/sour_grape/post/aec99f36-cfa5-48d2-bb66-43884f06351b/image.png" alt="">
이 <strong>비숍책</strong>과 </p>
<p>오늘 리뷰할 머피책을 추천 해준다.</p>
<p>둘 다 좋은 책이긴 한데 개인 취향인것 같다. 나는 비숍책은 위 번역본으로 봤고 머피책은 원서 pdf로 봤는데 <strong>머피책이 이해가 더 잘됐다.</strong></p>
<h2 id="머피책의-특징">머피책의 특징</h2>
<p>프로젝트에 베이지안 뉴럴 네트워크와 베이지안 최적화를 적용하기 위해서 머피책을 보고 있는데</p>
<p>이런 목적성이 있으면 정말 좋다고 느낄 수 있다. 머피책의 목차를 보자.</p>
<ol>
<li>Introduction</li>
<li>fundamental</li>
<li>inference</li>
<li>prediction</li>
<li>generation</li>
<li>discovery</li>
<li>action</li>
</ol>
<p><strong>머피책은 하나의 큰 흐름으로 구성되어 있다.</strong></p>
<p>대부분의 머신러닝 책이 분야별 주제를 모아놔서 백과사전 같은 느낌의 책이 많은데
머피책은 처음부터 끝까지 <strong>확률론적 머신러닝이란 큰 흐름을 유지한다</strong>.
책 제목과 내용이 정말 일관되게 일치하는 책은 오랜만이다. 제목 잘 지었다.</p>
<p>처음부터 여러 확률분포를 설명해준다. 왜? 뒤에서 계속 쓸거니까.
그 다음 베이지안에 대한 설명해주고 그걸 기본으로 깔고 머신러닝을 확률론적으로 해석한다.</p>
<p>그래서 처음부터 끝까지 하나의 큰 흐름이 계속 이어져서 좋다. <strong>읽을때 내가 이 확률 이론과 머신러닝의 관점, 그리고 내가 실제 프로젝트에 어떻게 적용할까? 3가지를 중점적으로 생각하며 읽으면 굉장히 좋은 책 이다.</strong></p>
<p><strong>내용 자체는 깊진 않다.</strong> 각 상황별로 이런 저런 솔루션이 있다고 간단하게 소개하는 정도다. 그래도 내용이 많은 편이라 2달 정도면 충분히 읽을만한 책이라고 생각한다.</p>
<h1 id="추천-상황">추천 상황</h1>
<p><strong>추천하고 싶은 상황</strong>은 </p>
<ol>
<li><p>미적, 선대, 해석, 위상수학 등등 수학적 기초가 있는데 확률론은 잘 몰라서 입문하고 싶은 경우</p>
</li>
<li><p>딥러닝은 많이 아는데 확률론적인 관점이 뭔지 궁금한 경우. 궁금하지 않아도  확률론적  관점을 모르면 보는걸 추천한다.</p>
</li>
<li><p>가벼운 마음으로 딥러닝 정리를 하고 싶다.</p>
</li>
</ol>
<p><strong>보면 안되는 상황</strong></p>
<ol>
<li><p>딥러닝 프로젝트를 진행해본 경험이 없다</p>
</li>
<li><p>수학적인 기초가 없다</p>
</li>
<li><p>딥러닝 논문을 많이 읽어보지 않았다.</p>
</li>
</ol>
<p>1,2 같은 경우는 얻어갈게 없어서 추천하지 않는다.</p>
<p>3의 경우 머피책 읽을 바에 관심 있는 논문 읽는게 횔씬 좋다.</p>
<h1 id="총평">총평</h1>
<p>입문자용으로 추천되곤 하는 데 절대 입문자용은 아니며(내용이 많음)</p>
<p>업계 종사자면 머리 식힐겸, 인공지능 학과생이면 독학으로 한 학기 정도 투자할만 하다.</p>
<p>내용은 정말 알차서 꼭 한 번 읽을만하다. 논문처럼 필수는 아니고 그냥 읽으면 좋을만한 책</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[음악 분류 딥러닝을 만들자(41) - Predicting Neural Network Accuracy from Weights 논문 리뷰, accuracy, latency predictor]]></title>
            <link>https://velog.io/@sour_grape/%EC%9D%8C%EC%95%85-%EB%B6%84%EB%A5%98-%EB%94%A5%EB%9F%AC%EB%8B%9D%EC%9D%84-%EB%A7%8C%EB%93%A4%EC%9E%9041-Predicting-Neural-Network-Accuracy-from-Weights-%EB%85%BC%EB%AC%B8-%EB%A6%AC%EB%B7%B0-accuracy-latency-predictor</link>
            <guid>https://velog.io/@sour_grape/%EC%9D%8C%EC%95%85-%EB%B6%84%EB%A5%98-%EB%94%A5%EB%9F%AC%EB%8B%9D%EC%9D%84-%EB%A7%8C%EB%93%A4%EC%9E%9041-Predicting-Neural-Network-Accuracy-from-Weights-%EB%85%BC%EB%AC%B8-%EB%A6%AC%EB%B7%B0-accuracy-latency-predictor</guid>
            <pubDate>Wed, 13 Nov 2024 06:15:29 GMT</pubDate>
            <description><![CDATA[<h1 id="cnn-모델의-성능-평가">CNN 모델의 성능 평가</h1>
<p>학습된 CNN의 모델 평가는 다양한 지표로 이루어지며 가장 직관적으로 사용되는 지표는 accuracy다. </p>
<p>딥러닝을 처음 배울 땐 accuracy, f1 score, precision, recall 이 4개를 평가지표로 배우고 accuracy를 보완한다는 느낌으로 다른 지표를 배우니 자칫 accuracy가 덜 떨어진 지표로 생각될 수 있다(내가 그랬음...) </p>
<p>papers with code의 benchmark도 그렇고 논문들을 읽어봐도 대부분 지표를 <strong>top-1 accuracy</strong>를 대표적으로 사용한다. </p>
<p>Nas 역시 마찬가지다. 진화적 알고리즘이던 강화학습을 이용하던 지표를 accuracy로 정하고 들어간다.</p>
<p>따라서 CNN 모델이 accuracy를 어떻게 평가하는지에 대한 이해와 이 accuracy를 어떻게 효율적으로 할 것 인지 연구하는게 중요하다고 생각된다.</p>
<h2 id="전통적인-cnn의-학습-방법과-성능-평가-방법">전통적인 CNN의 학습 방법과 성능 평가 방법</h2>
<p>이 시리즈는 기본적인 CNN의 지식이 있는 상태를 전제로 쓰여져서 자세하게 설명하진 않겠다.</p>
<p><img src="https://velog.velcdn.com/images/sour_grape/post/304705ba-b3f4-454d-9e7d-5a10f455aec5/image.png" alt=""></p>
<p>대략적으로 layer 한 개당 위와 같은 연산을 치룬다. 학습은 forward, back이 한 사이클로 이루어져 약 2배의 FLOPS를 가진다.</p>
<p>다루고 있는 논문의 목적은 meta값을 분석해 딥러닝의 본질적인 이해를 하는 것이다.</p>
<p>논문에선 paramter, weight, distillation, cnn zoo 등 여러 가지를 강조하고 있지만 우리가 Nas에서 주목할 부분은 weight를 이용해 accuracy를 추정하는 방법이다.</p>
<p>후에 다루겠지만 weight를 이용해 accuracy를 추정하는 방법은 입력 데이터에서 accuracy를 추정하는 방법보단 훨씬 효율적이다.</p>
<h1 id="파라미터-가중치-결과값의-관계">파라미터, 가중치, 결과값의 관계</h1>
<p>딥러닝 과정을 최대한 간단하게 <strong>뭉게면 그냥 매핑, 다변수 함수</strong>로 생각할 수 있다.</p>
<p>딥러닝 정의역은 paramter, weight라면 공역은 그냥 accuracy와 같은 성능지표라고 뭉갤수 있다. </p>
<p>정확하게 설명해보자.</p>
<p><img src="https://velog.velcdn.com/images/sour_grape/post/3332cabf-eb1b-4c97-9945-5236765fd8e7/image.png" alt="">
p는 X, Y의 곱집합에서 정의되는 분포라고 정하자.</p>
<p>X는 input, Y는 output이라고 했을 때 우리가 P분포에서 뽑을 수 있는(sample) 경우의 수는 다음과 같이 정의할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/sour_grape/post/cf09b505-7626-46de-9ebe-8ec502f3d220/image.png" alt=""></p>
<p>X, Y의 관계를 분류 문제로 생각해보면 X를 넣으면 결과값인 label, 즉 Y가 나오게 된다.</p>
<p>우리는 샘플링을 통해 Sn을 뽑고 CNN의 input 데이터로 사용한다.
<strong>Sn과 hyperparameter(λ)를 초기화해서 학습을 한 뒤 결과로 W vector</strong>를 얻는다. </p>
<p>이를 간단하게 함수로 표현하면 다음과 같다.</p>
<p><img src="https://velog.velcdn.com/images/sour_grape/post/63bbc6f8-72f9-4457-a62f-931844a21704/image.png" alt=""></p>
<p>여기서 A는 학습 과정을 나타낸다.</p>
<p>학습을 마친 뒤 다시 input data를 넣어서 forward 해주면 결과값 Y를 얻는게 딥러닝 과정인데 다음과 같이 표현한다.</p>
<p><img src="https://velog.velcdn.com/images/sour_grape/post/7b3ff642-4a40-4729-a527-a62f2c558e0b/image.png" alt=""></p>
<p>이때 Y의 라벨이 실제 데이터의 라벨과 얼마나 맞는 지 따져 전체적인 비율을 구하면 accuracy를 얻는다. </p>
<p><img src="https://velog.velcdn.com/images/sour_grape/post/8a83115a-a0b6-4c9c-93b7-ed9e256b7b4b/image.png" alt=""></p>
<p>함수 1은 y끼리 일치하면 결과값이 1인 함수다.</p>
<p>accuracy의 기댓값은 ** W, Sn**의 함수로 정해진다.</p>
<p><img src="https://velog.velcdn.com/images/sour_grape/post/4824ac0a-3cc1-424f-995f-569d9e6590ac/image.png" alt=""></p>
<p>CNN의 데이터 전처리에서 우리는 이미 데이터의 분포, 즉 입력 데이터와 출력 데이터의 라벨을 정해놓고 모든 걸 진행한다. 따라서 Sn은 정해진 값으로 <strong>accuracy는 단순히 W에 대한 매핑</strong>이 된다.</p>
<p><img src="https://velog.velcdn.com/images/sour_grape/post/14aba310-f6b8-47b2-8c1d-ae5106e232af/image.png" alt=""></p>
<p>앞선 사실을 통해 acuuracy는 W에 대한 함수로써 우리의 목표는 accuracy predictor를 사용하여 weight를 이용해 accuracy를 측정하는 것이다.</p>
<p><img src="https://velog.velcdn.com/images/sour_grape/post/43a77abf-6315-4141-a4b3-680e3466040b/image.png" alt=""></p>
<h1 id="f의-학습법">F의 학습법</h1>
<p>논문에서 3가지 predictor의 학습을 비교하여 유용한 학습법을 뽑았다.</p>
<p>logit-linear model (<strong>L-Linear</strong>), gradient boosting machine
using regression trees (<strong>GBM</strong>), and a fully-connected <strong>DNN</strong></p>
<p>이 3가지를 비교하였지만 나는 여기에 Bayesian Neural Network, Gaussian Process (GP) Regression을 추가하여 비교할 것이다.</p>
<p>자세한 학습 환경과 가정, 구현은 다음 시간에 살펴보겠다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[*음악 분류 딥러닝을 만들자(40) - evolution Nas]]></title>
            <link>https://velog.io/@sour_grape/%EC%9D%8C%EC%95%85-%EB%B6%84%EB%A5%98-%EB%94%A5%EB%9F%AC%EB%8B%9D%EC%9D%84-%EB%A7%8C%EB%93%A4%EC%9E%9040-evolution-Nas</link>
            <guid>https://velog.io/@sour_grape/%EC%9D%8C%EC%95%85-%EB%B6%84%EB%A5%98-%EB%94%A5%EB%9F%AC%EB%8B%9D%EC%9D%84-%EB%A7%8C%EB%93%A4%EC%9E%9040-evolution-Nas</guid>
            <pubDate>Thu, 07 Nov 2024 07:18:50 GMT</pubDate>
            <description><![CDATA[<h1 id="굉장히-주관적인-리뷰">굉장히 주관적인 리뷰</h1>
<p>사실 once-for-all 논문의 방법론에 Regularized Evolution for Image Classifier Architecture Search란 논문의 evolution nas를 이용했다고 해서 살펴보았다.</p>
<p>논문이나 글 들을 보면 가끔 열받을 때가 있다.
본인이 맞다고 정해놓고 글을 전개하는데 내가 볼땐 아니면 굉장히 열을 받는다.</p>
<p>이번 논문을 굳이 리뷰하는 이유는 논문을 읽다가 굉장히 열을 받았기 때문이다.
열받은 내용은 후에 서술하겠지만 소위말해 굉장이 긁혔기 때문에 논문에 대해서 비판도 아니고 비난에 가깝게 서술할 것이다. 참고하여 읽기 바란다.</p>
<h1 id="regularized-evolution-for-image-classifier-architecture-search">Regularized Evolution for Image Classifier Architecture Search</h1>
<p>란 논문을 리뷰하겠다.</p>
<h2 id="evolutionary-algorithm">Evolutionary Algorithm</h2>
<p>우선 기본 진화적 nas에 대해 간단히 말하자면 크게 2가지이다.</p>
<ol>
<li><p>random sampling을 통해 search space에서 아키텍처를 2개 뽑는다. 그 후 각각 평가를 해 우수한 것만 남기고 나머지는 삭제한다.</p>
</li>
<li><p>우수한 것들을 기본으로 레이어들을 변이(mutation)하여 아키텍처에 추가한다. 그 후 1과 2를 반복한다.</p>
</li>
</ol>
<p>논문에선 여기에 aging이란 기법을 추가했다. 그 알고리즘은 아래와 같다.
<img src="https://velog.velcdn.com/images/sour_grape/post/bae7ab94-f9f5-4718-99e3-d89ea75a5352/image.png" alt=""></p>
<p>기본적인 evolution nas에서 초기에 뽑은 아키텍처들이 변이(mutation)되고 그 변이체들이 또 비교되고 이런식으로 시작하다보니 잘못하면 초기 아키텍처들 위주로 고정되어 버린다.</p>
<p>nas는 전체 가능성을 따져서 최선의 아키텍처를 찾는 방식인데 초기에 뽑은 아키텍처에 집중하는건 안된다.</p>
<p><strong>그래서 초기에 생성한 아키텍처들에게 패널티를 주는거다. 나이를 매겨서 너무 늙었다면 삭제하는 식으로</strong></p>
<p>나이를 줘서 초기 아키텍처 위주로 진행되는 진화 알고리즘을 보완한 것이다.</p>
<h2 id="논문의-결과">논문의 결과</h2>
<p><img src="https://velog.velcdn.com/images/sour_grape/post/d98f98a9-10f1-4ec1-a122-d8a62c6ee8b0/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/sour_grape/post/47eae1f4-c5e4-44f3-b037-c500f329bc24/image.png" alt="">
<img src="https://velog.velcdn.com/images/sour_grape/post/eae80b7c-3cf6-45cc-90d9-38c8c32f5c1b/image.png" alt=""></p>
<p>이런 결과를 보여주면서 본인들의 알고리즘이 Reinforce Learning 이용한 Nas, Random Search를 이용한 Nas보다 우월하다고 주장하고 있다.</p>
<p>논문 저자들이 주장하고 있는 Regulazied evolution Nas의 장점은 크게 2가지</p>
<ol>
<li><p>다른 Nas들 보다 최적의 아키텍처를 찾는 <strong>시간이 빠르다.</strong></p>
</li>
<li><p>다른 Nas보다** flops, parameters의 수가 적다.**</p>
</li>
</ol>
<h1 id="내-비난">내 비난</h1>
<p>논문이 쓰여진 연도인 2019년 이미 Enas가 나왔고 one-shot nas, proxyless nas가 등장할 시기다.</p>
<p>이 논문의 가장 큰 문제점은 <strong>Weight shared</strong>를 사용하지 않았다는 점이다.</p>
<p>따라서 효율은 개나줌. </p>
<p>그리고 문제점, random search를 이용했다는 점인데 이게 쓰일 시기엔 weight shared과 random search 간의 비교가 별로 없었나보다.</p>
<p>random search보다 weight shared가 우월하다는 논문들이 많이 나왔고 현재 시점에 Nas의 효율을 따지는 거의 모든 논문들은 Weight shared를 포함하고 있다.</p>
<p>그리고 2번째 문제점</p>
<p>결과가 좋다는 이유가 다른 Nas보다 빠르다는 점인데 <strong>당연한 거 아닌가?</strong>
evolution Nas는 방법은 <strong>초기부터 random search를 해서 바로 평가를 한다는 점</strong>이다.</p>
<p>일반적인 RL을 이용한 Nas의 경우 모든 가능성을 조사하는 전수 조사 방법이다.
Nas는 모든 가능성 중 가장 최선의 nas를 찾는게 주 목표여서 전수 조사를 하되 효율적으로 하는게 다른 Nas의 방법론이다.</p>
<p>반면 evolution nas는 <strong>전수 조사가 아닌 일부만 뽑아서 대충 발전시켜서 최선이라 생각하자가 방법론이다.</strong></p>
<p>다시 그래프를 보자</p>
<p><img src="https://velog.velcdn.com/images/sour_grape/post/d98f98a9-10f1-4ec1-a122-d8a62c6ee8b0/image.png" alt=""></p>
<p>Evolution이 왜 초반에 accuaracy가 높냐? 당연히 다 조사 안하고 초반에 뽑은 것 중 괜찮은것만 남겨서 그런 것 이다. 당연한 거 아님?</p>
<p>그래프를 보면 RL과 Evolution 둘 다 시간이 충분히 증가하면 비슷한 accuracy를 갖는걸 볼 수 있다.</p>
<p>내가 왜 열 받았냐? 이 <strong>그래프가 굉장히 naive한 결과</strong>기 때문</p>
<p>이 그래프는 그냥 초반에 운이 좋아서 좋은 아키텍처를 뽑았고 그걸 발전시킨 그래프로 본인들의 방법론 중 <strong>가장 운이 좋은 케이스를 결과랍시고 소개했기 때문</strong>에 내가 빡친 것 이다.</p>
<p>두 번째로 효율성을 따지는 부분에서 flops와 parameter수를 말했는데 weight shared를 안 쓴 시점에서 weight를 일일이 초기화 할 텐데 뭐가 효율적이란 말인가?</p>
<p>최종 아키텍처의 flops나 parameter의 수가 중요해? weight를 일일이 초기화 할게 뻔해서 느릴텐 데</p>
<p>그리고 요즘엔 flops, paramter은 효율성을 따지는 지표에서 비주류가 되었다.</p>
<p>차라리 mobileNetV1 같이 경량화 테크닉을 만드는 논문이라면 충분히 논리적인 결과이지만 search space를 찾는 Nas에서 flops, paramter를 따지는 건 미친소리라고 생각한다.</p>
<p>차라리 Enas 이전의 초기 Nas였으면 그냥 넘어가겠으나 비슷한 시기에 나온 Mnas, oneshot, froxyless Nas와 너무 비교가 되는 열등한 논문이라고 생각한다.</p>
]]></description>
        </item>
    </channel>
</rss>