<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>모카의 촉촉한 개발 로그</title>
    <link>https://softmoca.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Wed, 15 Jul 2026 21:24:24 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>softmoca__</managingEditor>
    <image>
      <title>모카의 촉촉한 개발 로그</title>
      <url>https://tistory1.daumcdn.net/tistory/6859330/attach/5515d439d0204fa083016f0154180f16</url>
      <link>https://softmoca.tistory.com</link>
    </image>
    <item>
      <title>AWS SAA-C03 자격증 합격 후기</title>
      <link>https://softmoca.tistory.com/621</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1332&quot; data-origin-height=&quot;1352&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOQlUJ/dJMcabXktk5/vlbqaK4DFH5iFLodetdfW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOQlUJ/dJMcabXktk5/vlbqaK4DFH5iFLodetdfW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOQlUJ/dJMcabXktk5/vlbqaK4DFH5iFLodetdfW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOQlUJ%2FdJMcabXktk5%2FvlbqaK4DFH5iFLodetdfW1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1332&quot; height=&quot;1352&quot; data-origin-width=&quot;1332&quot; data-origin-height=&quot;1352&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;미루기만 하던 자격증을 10일 만에 끝낼 수 있었던 이유 &amp;mdash; JSCODE 강의 추천&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS SAA-C03 자격증은 반년 전부터 계속 머릿속에만 있던 숙제였다.&lt;br /&gt;&amp;ldquo;따야지&amp;rdquo;라는 생각은 있었지만, 늘 다음으로 미뤘다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유는 분명했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;블로그 후기마다 등장하는 &lt;b&gt;수십~수백 시간의 공부량&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;끝도 없이 등장하는 서비스 이름들&lt;/li&gt;
&lt;li&gt;유튜브, 덤프, 문제풀이, 실습까지 이어지는 &lt;b&gt;과도한 준비 루트&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;솔직히 말하면, 자격증 하나에 이렇게까지 해야 하나? 라는 생각이 컸다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;우아한테크코스 최종 결과 대기, 그리고 공백의 시간&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 다시 SAA를 떠올리게 된 계기는 &lt;b&gt;우아한테크코스 최종 결과를 기다리던 시기&lt;/b&gt;였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과를 기다리는 시간은 생각보다 훨씬 길고, 무엇보다 마음이 불안했다.&lt;br /&gt;아무것도 손에 잡히지 않는 상태가 이어졌고, 이대로 시간을 흘려보내는 게 오히려 더 힘들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 결심했다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;ldquo;강제로라도 집중할 무언가를 만들자.&amp;rdquo;&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 선택한 게 AWS SAA-C03 자격증이었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;다시 마주한 SAA, 그리고 JSCODE 강의&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 자료를 찾아보던 중 &lt;b&gt;JSCODE의 SAA-C03 강의&lt;/b&gt;를 보게 됐다.&lt;br /&gt;솔직히 처음엔 반신반의했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 강의 목차와 구성, 그리고 설명 방향을 보면서 생각이 바뀌었다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;무작정 서비스 나열 ❌&lt;/li&gt;
&lt;li&gt;덤프 위주 접근 ❌&lt;/li&gt;
&lt;li&gt;&amp;ldquo;이건 다 알아야 합니다&amp;rdquo; 식의 압박 ❌&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;시험의 성격을 먼저 설명&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&amp;ldquo;어디까지 이해하면 충분한가&amp;rdquo;를 명확히 제시&lt;/li&gt;
&lt;li&gt;서비스 하나하나를 &lt;b&gt;시험 관점에서 연결&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 강의는 지식 전달보다는 &lt;b&gt;시험을 치르는 관점과 사고 흐름을 먼저 만들어주는 강의&lt;/b&gt;였다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;수강 경험&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 JSCODE 로드맵을 따라가며 모든 강의를 빠짐없이 수강 중인데, 이번 SAA 강의도 새로 올라오자마자 바로 수강했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반년 동안 미뤄왔던 자격증이 &lt;b&gt;강의를 듣고 나니 3일도 안 돼 정리되었다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 남은 7일간은 강의에서 알려준 덤프 사이트들로 실전 감각과 큰 그림의 살을 조금씩 더 붙여가는 시간을 가졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 인상 깊었던 점은 다음이었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 압도적인 공부량이 사라졌다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 후기들에서 느끼던 &amp;ldquo;이만큼은 해야 한다&amp;rdquo;는 부담이 없었다.&lt;br /&gt;강의에서 이미&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;필요한 범위&lt;/li&gt;
&lt;li&gt;시험에 나오는 패턴&lt;/li&gt;
&lt;li&gt;깊게 파야 할 부분 / 가볍게 넘어가도 되는 부분&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;을 명확히 구분해줬기 때문이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. &amp;lsquo;시험을 이해한다&amp;rsquo;는 게 이런 거구나&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순 암기가 아니라 &lt;b&gt;문제가 왜 이렇게 출제되는지&lt;/b&gt;가 보이기 시작했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;덕분에 처음 보는 문제도 &amp;ldquo;아, 이건 이걸 묻는 거구나&amp;rdquo; 하고 접근할 수 있었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.&amp;nbsp; 시간과 정신적 에너지를 아껴줬다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SAA는 &lt;b&gt;합격/불합격으로만 나뉘는 시험&lt;/b&gt;이다.&lt;br /&gt;나는 이 시험에 인생을 걸고 싶지 않았다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;불필요하게 많은 시간 ❌&lt;/li&gt;
&lt;li&gt;끝없는 문제 풀이 ❌&lt;/li&gt;
&lt;li&gt;불안감을 키우는 공부 ❌&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSCODE 강의는 이 점을 정확히 짚어줬고, 덕분에 &lt;b&gt;최소한의 투자로 안정적인 합격&lt;/b&gt;이 가능했다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;시험 결과 &amp;mdash; 744점으로 합격&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로 &lt;b&gt;744점으로 SAA-C03에 합격&lt;/b&gt;했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고득점을 목표로 한 준비는 아니었지만, 강의에서 말해준 방향 그대로만 따라갔을 뿐인데 충분히 안정적인 점수로 합격할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 점수는 개인적으로 &amp;ldquo;이 강의 방향이 틀리지 않았다는 증거&amp;rdquo;라고 느꼈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역시나 믿고 듣는 JSCODE...!&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;이런 분들에게 JSCODE SAA 강의를 추천하고 싶다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 강의는 특히 아래 유형의 사람들에게 잘 맞는다고 생각한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SAA를 &lt;b&gt;계속 미루고 있는 사람&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;공부량에 압도돼 시작조차 못 하고 있는 사람&lt;/li&gt;
&lt;li&gt;자격증에 &lt;b&gt;과도한 시간과 에너지를 쓰고 싶지 않은 사람&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&amp;ldquo;왜 이걸 공부해야 하는지&amp;rdquo;부터 알고 싶은 사람&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 AWS 서비스를 깊게 파고들고 싶은 분&lt;/li&gt;
&lt;li&gt;초고득점 자체가 목표인 분&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에게는 다른 루트가 더 맞을 수도 있다고 생각한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;마무리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우아한테크코스 결과를 기다리며 불안했던 시간을 그냥 흘려보내지 않고 &lt;b&gt;의미 있는 결과&lt;/b&gt;로 바꿀 수 있어 더 만족스러웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSCODE 강의 덕분에 미루기만 하던 SAA 자격증을 짧은 시간 안에, 그리고 무엇보다 &lt;b&gt;편안한 마음으로&lt;/b&gt; 마무리할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>모카스터디</category>
      <author>softmoca__</author>
      <guid isPermaLink="true">https://softmoca.tistory.com/621</guid>
      <comments>https://softmoca.tistory.com/621#entry621comment</comments>
      <pubDate>Thu, 29 Jan 2026 11:55:19 +0900</pubDate>
    </item>
    <item>
      <title>[우아한테크코스 8기] 최종 코딩테스트 후기 및 최종 합격</title>
      <link>https://softmoca.tistory.com/619</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;시작하며&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;10월 14일 부터 1월 10일까지 프리코스, 오픈미션, 마지막 최종코딩테스트까지 3개월의 대장정을 마쳤다 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매번 회고 마다 느끼지만 몰입의 힘은 정말 대단하고 신기한 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반팔 반바지를 입고 에어컨 바람을 맞으며 시작했던 우아한테크코스 8기 선발 과정은 어느새 패딩을 입고 난방을 켜야 하는 계절을 지나 새해와 함께 끝을 맺었다.&lt;br /&gt;시간이 이렇게까지 압축되어 흐를 수 있다니.. 타임머신? 정신과시간의방? 을 경험 한듯 1년의 1/4인 한 분기가 순식간에 지나갔다 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 만큼 후회없이 모든 걸 하얗게 불태웠다 !!&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 마냥 불태운 것만은 아니다.&lt;br /&gt;매주 고민했던 흔적들, 시행착오와 깨달음, 그리고 그때의 생각들은 모두 블로그라는 공간에 차곡차곡 기록으로 남아 있다.&lt;br /&gt;&lt;b&gt;지나가버린 시간이 아니라, 언제든 다시 꺼내볼 수 있는 자산으로 남았다는 점&lt;/b&gt;이 더 의미 있게 느껴진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이렇게나 높은 몰입을 통해 하나에 온힘을 다 쏟아 부은 경험이 너무 소중하다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나를 이렇게 성장시켜주고, 이 과정 자체를 즐길 수 있게 해준 &lt;b&gt;우아한테크코스 운영진분들에게 늘 감사한 마음&lt;/b&gt;이 든다.&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;무엇보다 다시금 그간의 기록을 돌아보며, &lt;b&gt;포기하지 않고 끝까지 잘 완주해낸 나 자신&lt;/b&gt;에게도 &amp;ldquo;정말 잘&amp;nbsp; &amp;nbsp; &amp;nbsp;해냈다&amp;rdquo;는 말을 해주고 싶다.&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 경험은 분명, 앞으로의 선택과 도전 앞에서 나의 삶 전반을 관통하며 다시 나를 움직이게 만들어 줄 중요한 기준이 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1차 합격&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpnMUE/dJMcaiB0OHq/aGCnM4jOOEtEA7CWCbAntk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpnMUE/dJMcaiB0OHq/aGCnM4jOOEtEA7CWCbAntk/img.png&quot; data-origin-width=&quot;597&quot; data-origin-height=&quot;413&quot; data-is-animation=&quot;false&quot; style=&quot;width: 50.1291%; margin-right: 10px;&quot; data-widthpercent=&quot;50.72&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpnMUE/dJMcaiB0OHq/aGCnM4jOOEtEA7CWCbAntk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcpnMUE%2FdJMcaiB0OHq%2FaGCnM4jOOEtEA7CWCbAntk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;597&quot; height=&quot;413&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cl9HOC/dJMcagdaOVi/WksGvtDwJL4eOkWrs11T3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cl9HOC/dJMcagdaOVi/WksGvtDwJL4eOkWrs11T3k/img.png&quot; data-origin-width=&quot;618&quot; data-origin-height=&quot;440&quot; data-is-animation=&quot;false&quot; style=&quot;width: 48.7081%;&quot; data-widthpercent=&quot;49.28&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cl9HOC/dJMcagdaOVi/WksGvtDwJL4eOkWrs11T3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcl9HOC%2FdJMcagdaOVi%2FWksGvtDwJL4eOkWrs11T3k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;618&quot; height=&quot;440&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오픈&amp;nbsp;미션를&amp;nbsp;마친&amp;nbsp;후,&amp;nbsp;매일&amp;nbsp;10시간&amp;nbsp;씩&amp;nbsp;최종코딩테스트&amp;nbsp;준비를&amp;nbsp;하던&amp;nbsp;중&amp;nbsp;어느덧&amp;nbsp;1차&amp;nbsp;합격자&amp;nbsp;발표날이&amp;nbsp;되었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1차&amp;nbsp;합격과&amp;nbsp;최종합격&amp;nbsp;모두&amp;nbsp;결과를&amp;nbsp;기다리는&amp;nbsp;동안&amp;nbsp;&lt;b&gt;조금이나마&amp;nbsp;의심과&amp;nbsp;불안,&amp;nbsp;후회가&amp;nbsp;없도록&amp;nbsp;최선을&amp;nbsp;다해&amp;nbsp;자신감&lt;/b&gt;이&amp;nbsp;있었지만,&amp;nbsp;당일&amp;nbsp;아침부터&amp;nbsp;손이&amp;nbsp;저릿저릿하며&amp;nbsp;심장이&amp;nbsp;붕떠있는&amp;nbsp;느낌이&amp;nbsp;들었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서&amp;nbsp;늘&amp;nbsp;오전&amp;nbsp;9시에&amp;nbsp;5시간동안&amp;nbsp;1문제를&amp;nbsp;풀고&amp;nbsp;1시간을&amp;nbsp;회고하고&amp;nbsp;정리하는&amp;nbsp;시간을&amp;nbsp;가졌지만,&amp;nbsp;29일&amp;nbsp;만큼은&amp;nbsp;이&amp;nbsp;루틴을&amp;nbsp;1시간&amp;nbsp;늦게&amp;nbsp;시작했다.&amp;nbsp;역시나&amp;nbsp;문제&amp;nbsp;하나를&amp;nbsp;시작하니&amp;nbsp;5시간&amp;nbsp;동안&amp;nbsp;아무런&amp;nbsp;감정을&amp;nbsp;느끼지&amp;nbsp;못한채&amp;nbsp;온전히&amp;nbsp;문제를&amp;nbsp;푸는것에&amp;nbsp;집중할&amp;nbsp;수&amp;nbsp;있었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고&amp;nbsp;대망의&amp;nbsp;오후&amp;nbsp;3시가&amp;nbsp;되자&amp;nbsp;카카오톡&amp;nbsp;우테코&amp;nbsp;오픈톡방에는&amp;nbsp;벌써&amp;nbsp;여러&amp;nbsp;합격자분들의&amp;nbsp;소식이&amp;nbsp;전해졌지만&amp;nbsp;나의&amp;nbsp;메일창에는&amp;nbsp;아무런&amp;nbsp;소식이&amp;nbsp;없었고,&amp;nbsp;약&amp;nbsp;10분&amp;nbsp;뒤&amp;nbsp;합격&amp;nbsp;메일을&amp;nbsp;확인했다.&amp;nbsp;1000명이&amp;nbsp;넘는&amp;nbsp;모든&amp;nbsp;지원자에게&amp;nbsp;메일을&amp;nbsp;보내다보니&amp;nbsp;늦게&amp;nbsp;오는&amp;nbsp;경우가&amp;nbsp;있다는것과&amp;nbsp;그&amp;nbsp;순서가&amp;nbsp;합격의&amp;nbsp;유무와&amp;nbsp;관련이&amp;nbsp;없다는걸&amp;nbsp;알지만&amp;nbsp;새로고침을&amp;nbsp;반복해서&amp;nbsp;누르던&amp;nbsp;10분이&amp;nbsp;마치&amp;nbsp;플랭크를&amp;nbsp;하듯&amp;nbsp;아주&amp;nbsp;느리게&amp;nbsp;시간이&amp;nbsp;흘러갔고&amp;nbsp;손이&amp;nbsp;저릿저릿&amp;nbsp;심장이&amp;nbsp;쿵쾅거렸다.&amp;nbsp;작년&amp;nbsp;7기에는&amp;nbsp;오후&amp;nbsp;11시경에&amp;nbsp;결과가&amp;nbsp;나왔다는&amp;nbsp;분도&amp;nbsp;계셨고,&amp;nbsp;올해는&amp;nbsp;다음날&amp;nbsp;새벽에&amp;nbsp;연락이&amp;nbsp;왔다는&amp;nbsp;분도&amp;nbsp;계셨다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;작년 7기를 떨어진 이후 1년 가까운 시간을 8기 우테코 지원을 준비했고, 프리코스 기간 동안 오직 우테코 단 하나만을 위해 달려왔었기에 1차는 분명 합격할 것 이라는 확신이 있었지만, 매번 실제 결과를 보기 전까지는 늘 짜릿한 것 같다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한달간&amp;nbsp;미리&amp;nbsp;최종코딩테스트를&amp;nbsp;준비하며&amp;nbsp;점차&amp;nbsp;텐션이&amp;nbsp;떨어지고&amp;nbsp;이었지만,&amp;nbsp;실제&amp;nbsp;합격&amp;nbsp;소식을&amp;nbsp;눈으로&amp;nbsp;본&amp;nbsp;만큼&amp;nbsp;다시금&amp;nbsp;최종코딩테스트&amp;nbsp;준비에&amp;nbsp;대한&amp;nbsp;동기가&amp;nbsp;더욱&amp;nbsp;불탔다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;최종 코딩 테스트 준비&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작년 7기의 경우 1차 합격 발표가 나온 뒤 약 7일 정도 후 바로 코딩테스트를 쳐야했지만 올해는 약 10일 가량 시간이 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작년 코딩테스트 경험과 두번의 프리코스, 오픈미션의 자체 프리코스를 더해 &lt;b&gt;이미 프리코스 미션들을 최소 4번씩 풀어보았기 때문에 약간의 자신감&lt;/b&gt;이 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 1차 결과가 나온 이후 결과를 보고 준비를 할까도 싶었지만 &lt;b&gt;그 어떠한 후회도 남기고 싶지 않았고, 8기 프리코스를 정말 열심히 진행했기에 1차 합격에 대한 확신이 있어 그 어떤 타협과 실패에 대한 가정없이&lt;/b&gt; 오픈미션이 끝난 이틀 뒤부터 바로 최종코딩테스트 준비를 시작했다. 우선 풀어볼 문제들을 노션에 정리를 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 문제들은 우테코 깃허브나 구글링을 통해 찾을 수 있었지만 작년 7기 문제들은 찾을 수 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4주차 편의점 미션의 경우 작년 프리코스를 준비하며 정리를 해놓은것이 있었지만 최종 코딩테스트 문제를 찾을 수 없었다. &lt;b&gt;7기 최종 코딩테스트 문제는 제외를 할까도 싶었지만, 너무나 좋은 양질의 문제였어서 포기할 수 없고,&lt;/b&gt; 작년 7기 백엔드&lt;b&gt; 합격자분들의 깃허브와 합격후기들을 통해 문제를 만들어 보려했지만 너무 빈약&lt;/b&gt;했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Wr8go/dJMcafrNZeo/pvVC20CK2VJqfaHIylXqsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Wr8go/dJMcafrNZeo/pvVC20CK2VJqfaHIylXqsk/img.png&quot; style=&quot;width: 73.5609%; margin-right: 10px;&quot; data-widthpercent=&quot;74.43&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;514&quot; data-origin-width=&quot;690&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Wr8go/dJMcafrNZeo/pvVC20CK2VJqfaHIylXqsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWr8go%2FdJMcafrNZeo%2FpvVC20CK2VJqfaHIylXqsk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;690&quot; height=&quot;514&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwCmwp/dJMcagxtqYc/vwv6VQQVc7MwiS0LmgBHo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwCmwp/dJMcagxtqYc/vwv6VQQVc7MwiS0LmgBHo0/img.png&quot; style=&quot;width: 25.2763%;&quot; data-widthpercent=&quot;25.57&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;2556&quot; data-origin-width=&quot;1179&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwCmwp/dJMcagxtqYc/vwv6VQQVc7MwiS0LmgBHo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwCmwp%2FdJMcagxtqYc%2Fvwv6VQQVc7MwiS0LmgBHo0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1179&quot; height=&quot;2556&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 &lt;b&gt;7기 합격자분들의 합격 후기와 깃허브를 통해 메일로 직접 연락&lt;/b&gt;을 드렸고, &lt;b&gt;결국&amp;nbsp; 한 7기 크루분에게 문제 가이드 문서&lt;/b&gt;를 얻을 수 있었다.&lt;/p&gt;
&lt;p data-end=&quot;2613&quot; data-start=&quot;2472&quot; data-ke-size=&quot;size16&quot;&gt;거의 1년이 지난 자료임에도 불구하고 퇴근 후 시간을 써서 찾아봐 주셨고, 직접 가지고 계시지는 않았지만 주변 크루분들을 통해 자료를 전달해 주셨다.&lt;br /&gt;그리고 &lt;b&gt;꼭 합격한 뒤 커피챗을 신청해 달라는 말씀과 함께, 합격하면 밥을 사주겠다는 응원&lt;/b&gt;도 받았다.&lt;/p&gt;
&lt;p data-end=&quot;2661&quot; data-start=&quot;2615&quot; data-ke-size=&quot;size16&quot;&gt;너무 큰 힘이 되었고, 꼭 합격한 뒤에는 내가 밥을 대접하고 싶다는 생각이 들었다.&lt;/p&gt;
&lt;p data-end=&quot;2735&quot; data-start=&quot;2663&quot; data-ke-size=&quot;size16&quot;&gt;처음 문제 가이드를 보내주신 분뿐만 아니라, 다음 날에는 &lt;b&gt;연락을 드린 모든 분들로부터 답장을 받았고 많은 응원&lt;/b&gt;을 받을 수 있었다.&lt;/p&gt;
&lt;p data-end=&quot;2820&quot; data-start=&quot;2737&quot; data-ke-size=&quot;size16&quot;&gt;바쁘신 와중에 부담스러우실까 봐 최대한 공손하게 메일을 보냈는데, 모두들 따뜻하게 대해 주셔서 우테코 크루분들의 따뜻함을 다시 한번 느낄 수 있었다.&lt;/p&gt;
&lt;p data-end=&quot;2895&quot; data-start=&quot;2822&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;꼭 합격한 후 연락을 드린 분들께 안부 인사를 전하고, 시간이 된다면 함께 식사를 하며 여러 이야기를 나누고 싶다&lt;/b&gt;는 생각이 들었다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;문제 풀기&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QyAGD/dJMcafrNZHm/tS1kGZnx1uzSsWoYSdN6lk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QyAGD/dJMcafrNZHm/tS1kGZnx1uzSsWoYSdN6lk/img.png&quot; style=&quot;width: 35.2088%; margin-right: 10px;&quot; data-widthpercent=&quot;36.05&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;934&quot; data-origin-width=&quot;419&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QyAGD/dJMcafrNZHm/tS1kGZnx1uzSsWoYSdN6lk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQyAGD%2FdJMcafrNZHm%2FtS1kGZnx1uzSsWoYSdN6lk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;419&quot; height=&quot;934&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAdVTN/dJMcabwaxH3/eaRvPavOmaLPovWMiO0mU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAdVTN/dJMcabwaxH3/eaRvPavOmaLPovWMiO0mU0/img.png&quot; style=&quot;width: 37.332%; margin-right: 10px;&quot; data-widthpercent=&quot;38.22&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;719&quot; data-origin-width=&quot;342&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAdVTN/dJMcabwaxH3/eaRvPavOmaLPovWMiO0mU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAdVTN%2FdJMcabwaxH3%2FeaRvPavOmaLPovWMiO0mU0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;342&quot; height=&quot;719&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eAfzHo/dJMcabwaxKt/0tNCze9IE1WK1KhuIefyT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eAfzHo/dJMcabwaxKt/0tNCze9IE1WK1KhuIefyT1/img.png&quot; style=&quot;width: 25.1337%;&quot; data-widthpercent=&quot;25.73&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;840&quot; data-origin-width=&quot;269&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eAfzHo/dJMcabwaxKt/0tNCze9IE1WK1KhuIefyT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeAfzHo%2FdJMcabwaxKt%2F0tNCze9IE1WK1KhuIefyT1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;269&quot; height=&quot;840&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그 후 &lt;b&gt;총 8년간의 모든 우테코 프리코스 미션들을 최소 다섯 번 이상씩 풀어보며 회고와 정리의 시간&lt;/b&gt;을 가졌다.&lt;/p&gt;
&lt;p data-end=&quot;3028&quot; data-start=&quot;2974&quot; data-ke-size=&quot;size16&quot;&gt;처음 두 바퀴는 시간을 재지 않고, 기능 구현과 리팩토링, 테스트 코드 작성을 목표로 풀어보았다.&lt;/p&gt;
&lt;p data-end=&quot;3094&quot; data-start=&quot;3030&quot; data-ke-size=&quot;size16&quot;&gt;이 과정을 통해 헷갈리던 문법과 컨벤션을 노션에 정리했고, 시간이 오래 걸렸던 부분들을 복기하며 개선점을 기록했다.&lt;/p&gt;
&lt;p data-end=&quot;3159&quot; data-start=&quot;3096&quot; data-ke-size=&quot;size16&quot;&gt;리팩토링과 설계를 다양한 관점에서 개선할 수 있다는 것을 다시금 느끼며, 프리코스에서의 배움을 복습할 수 있었다.&lt;/p&gt;
&lt;p data-end=&quot;3159&quot; data-start=&quot;3096&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wtwDf/dJMcadU0BXN/EGEpg6ViwDJvjUjqFtz8sk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wtwDf/dJMcadU0BXN/EGEpg6ViwDJvjUjqFtz8sk/img.png&quot; style=&quot;width: 46.3439%; margin-right: 10px;&quot; data-widthpercent=&quot;46.89&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;960&quot; data-origin-width=&quot;507&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wtwDf/dJMcadU0BXN/EGEpg6ViwDJvjUjqFtz8sk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwtwDf%2FdJMcadU0BXN%2FEGEpg6ViwDJvjUjqFtz8sk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;507&quot; height=&quot;960&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pZFWq/dJMcabiD9Hs/tlQi0WwuaSdHKACEJV6Z81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pZFWq/dJMcabiD9Hs/tlQi0WwuaSdHKACEJV6Z81/img.png&quot; style=&quot;width: 52.4933%;&quot; data-widthpercent=&quot;53.11&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;779&quot; data-origin-width=&quot;466&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pZFWq/dJMcabiD9Hs/tlQi0WwuaSdHKACEJV6Z81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpZFWq%2FdJMcabiD9Hs%2FtlQi0WwuaSdHKACEJV6Z81%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;466&quot; height=&quot;779&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;3159&quot; data-start=&quot;3096&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;3226&quot; data-start=&quot;3161&quot; data-ke-size=&quot;size16&quot;&gt;그 후에는 5시간이라는 제한 속에서 다시 한 번 기능 구현, 리팩토링, 테스트 코드 작성을 목표로 문제를 풀어보았다.&lt;/p&gt;
&lt;p data-end=&quot;3308&quot; data-start=&quot;3228&quot; data-ke-size=&quot;size16&quot;&gt;이 과정을 통해 거의 필수적으로 적용되는 &lt;b&gt;여러 패턴과 코드 템플릿을 정리&lt;/b&gt;할 수 있었고, &lt;b&gt;실제 코딩 테스트에서의 행동 강령을 구체화&lt;/b&gt;할 수 있었다.&lt;/p&gt;
&lt;p data-end=&quot;3416&quot; data-start=&quot;3310&quot; data-ke-size=&quot;size16&quot;&gt;최종 코딩 테스트 시작 전의 &lt;b&gt;마음가짐, 시간대별 체크 사항, 상황별 행동 강령, 리팩토링 우선순위 등을 정리하며 긴장이나 잘못된 몰입으로 인해 절대 망하거나 포기하지 않기 위한 준비&lt;/b&gt;를 했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdTlau/dJMcaiPyhJc/m8uPkpUDA0j4weADBrlRj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdTlau/dJMcaiPyhJc/m8uPkpUDA0j4weADBrlRj0/img.png&quot; style=&quot;width: 59.2729%; margin-right: 10px;&quot; data-widthpercent=&quot;59.97&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;763&quot; data-origin-width=&quot;473&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdTlau/dJMcaiPyhJc/m8uPkpUDA0j4weADBrlRj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdTlau%2FdJMcaiPyhJc%2Fm8uPkpUDA0j4weADBrlRj0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;473&quot; height=&quot;763&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3Giv1/dJMcai9QEz7/J9I1d8C10YGafdgPkyRi20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3Giv1/dJMcai9QEz7/J9I1d8C10YGafdgPkyRi20/img.png&quot; style=&quot;width: 39.5643%;&quot; data-widthpercent=&quot;40.03&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;638&quot; data-origin-width=&quot;264&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3Giv1/dJMcai9QEz7/J9I1d8C10YGafdgPkyRi20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3Giv1%2FdJMcai9QEz7%2FJ9I1d8C10YGafdgPkyRi20%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;264&quot; height=&quot;638&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;3475&quot; data-start=&quot;3418&quot; data-ke-size=&quot;size16&quot;&gt;또한 공통적으로, 조금이라도 생각하는 시간을 단축하기 위해 자주 사용하는 내용들도 따로 정리해 두었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;최종 코딩테스트 후기&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cysoN2/dJMcaiaZXcq/5ZUeWkLtRkFuKAbuML9BN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cysoN2/dJMcaiaZXcq/5ZUeWkLtRkFuKAbuML9BN1/img.png&quot; data-origin-width=&quot;520&quot; data-origin-height=&quot;847&quot; data-is-animation=&quot;false&quot; style=&quot;width: 43.3158%; margin-right: 10px;&quot; data-widthpercent=&quot;43.83&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cysoN2/dJMcaiaZXcq/5ZUeWkLtRkFuKAbuML9BN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcysoN2%2FdJMcaiaZXcq%2F5ZUeWkLtRkFuKAbuML9BN1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;520&quot; height=&quot;847&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ew6Bzl/dJMcac9DrBh/gWjqntQ4ITcfbmna5zTMm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ew6Bzl/dJMcac9DrBh/gWjqntQ4ITcfbmna5zTMm0/img.png&quot; data-origin-width=&quot;650&quot; data-origin-height=&quot;826&quot; data-is-animation=&quot;false&quot; style=&quot;width: 55.5214%;&quot; data-widthpercent=&quot;56.17&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ew6Bzl/dJMcac9DrBh/gWjqntQ4ITcfbmna5zTMm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Few6Bzl%2FdJMcac9DrBh%2FgWjqntQ4ITcfbmna5zTMm0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;826&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;13시부터 18시까지 총 5시간 시험이 이루어졌으며, 이번 문제인 &lt;b&gt;'행성로또'는 3주차의 로또 문제와 거의 유사&lt;/b&gt;했다.&lt;/p&gt;
&lt;p data-end=&quot;264&quot; data-start=&quot;179&quot; data-ke-size=&quot;size16&quot;&gt;하지만 이번 8기 선발과정은 기존 프리코스와 다르게 4주차 대신 오픈미션이 진행되었던 만큼, 이번 8기 최종코딩테스트에는 달라진 점들이 몇 가지 있었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/erMWto/dJMcajgCZxl/D1heSkBH28RcJh9kha4Y4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/erMWto/dJMcajgCZxl/D1heSkBH28RcJh9kha4Y4K/img.png&quot; data-origin-width=&quot;373&quot; data-origin-height=&quot;194&quot; data-is-animation=&quot;false&quot; style=&quot;width: 59.4163%; margin-right: 10px;&quot; data-widthpercent=&quot;60.12&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/erMWto/dJMcajgCZxl/D1heSkBH28RcJh9kha4Y4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FerMWto%2FdJMcajgCZxl%2FD1heSkBH28RcJh9kha4Y4K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;373&quot; height=&quot;194&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bddJwY/dJMcagqHMDG/bxzxFHd4XQcDBMaXWRqFsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bddJwY/dJMcagqHMDG/bxzxFHd4XQcDBMaXWRqFsk/img.png&quot; data-origin-width=&quot;398&quot; data-origin-height=&quot;312&quot; data-is-animation=&quot;false&quot; style=&quot;width: 39.4209%;&quot; data-widthpercent=&quot;39.88&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bddJwY/dJMcagqHMDG/bxzxFHd4XQcDBMaXWRqFsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbddJwY%2FdJMcagqHMDG%2FbxzxFHd4XQcDBMaXWRqFsk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;398&quot; height=&quot;312&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;264&quot; data-start=&quot;179&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;318&quot; data-start=&quot;266&quot; data-ke-size=&quot;size16&quot;&gt;우선 기존에는 5시간 모두 문제를 풀고 최종 제출의 소감문을 최종 제출 시 첨부하면 끝이었다.&lt;/p&gt;
&lt;p data-end=&quot;379&quot; data-start=&quot;320&quot; data-ke-size=&quot;size16&quot;&gt;하지만 이번에는 17시까지 4시간 동안 문제를 풀어야 했고, 남은 1시간 동안 소감문을 작성하는 것이었다. 또한 &lt;b&gt;'도전 과제'가 추가&lt;/b&gt;되었다.&lt;/p&gt;
&lt;p data-end=&quot;456&quot; data-start=&quot;401&quot; data-ke-size=&quot;size16&quot;&gt;기본적으로 구현해야 하는 요구 사항을 모두 충족한 뒤 2가지 중 하나를 선택해서 도전을 해야 했다.&lt;/p&gt;
&lt;p data-end=&quot;456&quot; data-start=&quot;401&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;748&quot; data-origin-height=&quot;152&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4MPpq/dJMcaiINb86/LQ9uEFcCsAcFVuzpgFDTX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4MPpq/dJMcaiINb86/LQ9uEFcCsAcFVuzpgFDTX1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4MPpq/dJMcaiINb86/LQ9uEFcCsAcFVuzpgFDTX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4MPpq%2FdJMcaiINb86%2FLQ9uEFcCsAcFVuzpgFDTX1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;748&quot; height=&quot;152&quot; data-origin-width=&quot;748&quot; data-origin-height=&quot;152&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;456&quot; data-start=&quot;401&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;485&quot; data-start=&quot;458&quot; data-ke-size=&quot;size16&quot;&gt;도전 과제를 선택하는 데에는 큰 어려움이 없었다.&lt;/p&gt;
&lt;p data-end=&quot;527&quot; data-start=&quot;487&quot; data-ke-size=&quot;size16&quot;&gt;왜냐하면 문서 최앞단에 이미 '최종코딩테스트의 목적'이 있었기 때문이다.&lt;/p&gt;
&lt;p data-end=&quot;527&quot; data-start=&quot;487&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;546&quot; data-start=&quot;529&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[최종 코딩 테스트의 목적]&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-start=&quot;529&quot; data-end=&quot;546&quot; data-ke-size=&quot;size16&quot;&gt;&quot;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;프리코스에서는 3가지 기본 미션과 오픈 미션을 통해 주어진 요구 사항의 구현을 넘어 본인만의 도전을 함께 수행&lt;/b&gt;&lt;/span&gt;하였습니다. 최종 코딩 테스트는 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;이 경험을 본인의 힘으로 해냈음을 증명하는 시간&lt;/b&gt;&lt;/span&gt;입니다.&quot;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-start=&quot;529&quot; data-end=&quot;546&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;752&quot; data-start=&quot;662&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;나는 프리코스 과정, 특히 오픈미션을 진행하며 리팩토링과 테스트코드에 대해 가장 많은 도전과 경험을 하며 성장했기에 'TDD를 통한 리팩토링'을 보여주고자&lt;/b&gt; 했다.&lt;/p&gt;
&lt;p data-end=&quot;810&quot; data-start=&quot;754&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;무엇보다 내가 오픈미션의 주제로 TDD를 선택하게 된 첫 시작이자 핵심 이유가 '로또'였기 때문&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-end=&quot;861&quot; data-start=&quot;812&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;861&quot; data-start=&quot;812&quot; data-ke-size=&quot;size16&quot;&gt;그렇게 기능 구현 목록과 도전 과제를 결정한 후, TDD로 최종 코딩 테스트를 시작했다.&lt;/p&gt;
&lt;p data-end=&quot;954&quot; data-start=&quot;863&quot; data-ke-size=&quot;size16&quot;&gt;기존 로또 문제에서 약간 요구사항이 달라진 '행성 로또'는 &lt;b&gt;오픈미션을 통해 얻은 여러 교훈 중 '요구사항 분석 도구로서의 TDD'를 적용해보기 너무 좋은 문제&lt;/b&gt;였다.&lt;/p&gt;
&lt;p data-end=&quot;1107&quot; data-start=&quot;956&quot; data-ke-size=&quot;size16&quot;&gt;또한 모든 기능 구현을 마친 뒤, 제공된 테스트와 실제 콘솔 출력을 해보려던 차 출력 메서드를 수정할 수 없고, 정해진 메서드를 사용해야 했음을 다시금 발견했다. 하지만 &lt;b&gt;&quot;리팩토링 안전망으로서의 TDD&quot; 덕분에 큰 정신적 충격과 멘탈 붕괴 없이 빠르게 대처&lt;/b&gt;할 수 있었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;697&quot; data-origin-height=&quot;215&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/u6Zrd/dJMcafk2U39/kSQ5GQbElG88eNt5tWjwf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/u6Zrd/dJMcafk2U39/kSQ5GQbElG88eNt5tWjwf0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/u6Zrd/dJMcafk2U39/kSQ5GQbElG88eNt5tWjwf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fu6Zrd%2FdJMcafk2U39%2FkSQ5GQbElG88eNt5tWjwf0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;697&quot; height=&quot;215&quot; data-origin-width=&quot;697&quot; data-origin-height=&quot;215&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;1107&quot; data-start=&quot;956&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1107&quot; data-start=&quot;956&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/softmoca/java-planetlotto-8/tree/softmoca&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/softmoca/java-planetlotto-8/tree/softmoca&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1768532829822&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - softmoca/java-planetlotto-8&quot; data-og-description=&quot;Contribute to softmoca/java-planetlotto-8 development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/softmoca/java-planetlotto-8/tree/softmoca&quot; data-og-url=&quot;https://github.com/softmoca/java-planetlotto-8&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dILawS/dJMb8VNoRyW/9nulubjkmd8m5VXsf7XsJk/img.png?width=1200&amp;amp;height=600&amp;amp;face=988_120_1036_172,https://scrap.kakaocdn.net/dn/iNpzX/dJMb9dHhzhd/rDHNkRpPW4Y7asPDo404g0/img.png?width=1200&amp;amp;height=600&amp;amp;face=988_120_1036_172&quot;&gt;&lt;a href=&quot;https://github.com/softmoca/java-planetlotto-8/tree/softmoca&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/softmoca/java-planetlotto-8/tree/softmoca&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dILawS/dJMb8VNoRyW/9nulubjkmd8m5VXsf7XsJk/img.png?width=1200&amp;amp;height=600&amp;amp;face=988_120_1036_172,https://scrap.kakaocdn.net/dn/iNpzX/dJMb9dHhzhd/rDHNkRpPW4Y7asPDo404g0/img.png?width=1200&amp;amp;height=600&amp;amp;face=988_120_1036_172');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - softmoca/java-planetlotto-8&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to softmoca/java-planetlotto-8 development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-end=&quot;1107&quot; data-start=&quot;956&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1153&quot; data-start=&quot;1109&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1153&quot; data-start=&quot;1109&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222222; text-align: start;&quot;&gt;물론 시간 제한 속에서 더 개선하고 싶었던 부분들은 분명히 많이 남아있었다.&lt;/span&gt;&lt;span style=&quot;color: #222222; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;1153&quot; data-start=&quot;1109&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222222; text-align: start;&quot;&gt;하지만 그저 프리코스와 오픈미션에서 내가 경험하며 얻은 여러 배움들을 최종 시험에 온전히 잘 적용하며 그 효과를 느낀 것 만으로 시험을 잘 끝마쳤다 생각한다.&amp;nbsp; &lt;/span&gt;무엇보다 &lt;b&gt;오픈 미션에서 얻은 교훈을 너무나 잘 적용했다는 점에서 너무 너무너무 뿌듯&lt;/b&gt;했다 !&lt;/p&gt;
&lt;p data-end=&quot;1153&quot; data-start=&quot;1109&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1153&quot; data-start=&quot;1109&quot; data-ke-size=&quot;size16&quot;&gt;하지만 한편으로는, '프리코스와 오픈미션에서 경험한 TDD'를 그대로 보여주기보다 '필요한 기능을 빠르게 구현한 뒤, 미리 준비해 두었던 템플릿을 기반으로 리팩토링'했다면 결과물의 완성도는 더 높아질 수도 있었겠다는 아쉬움도 남았다.&lt;/p&gt;
&lt;p data-end=&quot;342&quot; data-start=&quot;228&quot; data-ke-size=&quot;size16&quot;&gt;그럼에도 불구하고 이번 최종 코딩 테스트에서 내가 가장 중요하게 여긴 것은 단순히 &amp;ldquo;더 잘 보이는 선택&amp;rdquo;이 아니라, &lt;b&gt;&quot;그동안 내가 실제로 고민하고, 부딪히며 체득한 경험들을 코드에 녹여내는 것&quot;&lt;/b&gt;이었다.&lt;/p&gt;
&lt;p data-end=&quot;452&quot; data-start=&quot;344&quot; data-ke-size=&quot;size16&quot;&gt;정답을 맞히는 시험이라기 보다,&lt;b&gt; 프리코스와 오픈미션을 통해 쌓아온 사고 과정과 태도를 나의 힘으로 증명하는 자리였다고 생각하면, 이번 선택과 과정은 충분한 의미&lt;/b&gt;가 있었다고 생각한다.&lt;/p&gt;
&lt;p data-end=&quot;502&quot; data-start=&quot;454&quot; data-ke-size=&quot;size16&quot;&gt;완벽하진 않았지만, 분명히 성장의 방향은 틀리지 않았다는 확신을 남겨준 시간이었다.&lt;/p&gt;
&lt;p data-end=&quot;1153&quot; data-start=&quot;1109&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1153&quot; data-start=&quot;1109&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/softmoca/java-planetlotto-8/tree/refactor_softmoca&quot;&gt;https://github.com/softmoca/java-planetlotto-8/tree/refactor_softmoca&lt;/a&gt;(코딩테스트 이후 추가 개선하고 싶었던 부분을 반영한 브랜치)&lt;/p&gt;
&lt;figure id=&quot;og_1768532956233&quot; contenteditable=&quot;false&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cUIxTq/dJMb8862uL1/dUSSMKKdnuOOsOQt6hM1Xk/img.png?width=1200&amp;amp;height=600&amp;amp;face=988_120_1036_172,https://scrap.kakaocdn.net/dn/cRTU6y/dJMb86OVl0O/Ikmttkjp6Nr7coN3p4YHlk/img.png?width=1200&amp;amp;height=600&amp;amp;face=988_120_1036_172&quot; data-og-url=&quot;https://github.com/softmoca/java-planetlotto-8&quot; data-og-source-url=&quot;https://github.com/softmoca/java-planetlotto-8/tree/refactor_softmoca&quot; data-og-host=&quot;github.com&quot; data-og-description=&quot;Contribute to softmoca/java-planetlotto-8 development by creating an account on GitHub.&quot; data-og-title=&quot;GitHub - softmoca/java-planetlotto-8&quot; data-og-type=&quot;object&quot; data-ke-align=&quot;alignCenter&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://github.com/softmoca/java-planetlotto-8/tree/refactor_softmoca&quot; data-source-url=&quot;https://github.com/softmoca/java-planetlotto-8/tree/refactor_softmoca&quot;&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - softmoca/java-planetlotto-8&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to softmoca/java-planetlotto-8 development by creating an account on GitHub.&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1153&quot; data-start=&quot;1109&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;마치며&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종 코딩테스트 제출 후 작성한 소감문으로 후기를 마치려한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작년 7기 프리코스와 최종 코딩테스트는 저에게&lt;b&gt; &amp;ldquo;나는 한번 꽂히면 끝까지 몰입할 수 있는 사람&amp;rdquo;&lt;/b&gt;이라는 사실을 처음으로 확인하게 해준 경험이었습니다.&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;동시에,&amp;nbsp;그&amp;nbsp;몰입이&amp;nbsp;항상&amp;nbsp;올바른&amp;nbsp;방향을&amp;nbsp;향하지는&amp;nbsp;않는다는&amp;nbsp;것도&amp;nbsp;배웠습니다.&lt;br /&gt;합격에&amp;nbsp;대한&amp;nbsp;압박감&amp;nbsp;속에서&amp;nbsp;저는&amp;nbsp;점점&amp;nbsp;정답을&amp;nbsp;맞히는&amp;nbsp;감각에&amp;nbsp;집착했고,&amp;nbsp;그&amp;nbsp;조급함은&amp;nbsp;설계와&amp;nbsp;원칙을&amp;nbsp;흔들었습니다.&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;단&amp;nbsp;하나의&amp;nbsp;실패한&amp;nbsp;테스트를&amp;nbsp;고치겠다는&amp;nbsp;마음이&amp;nbsp;전체&amp;nbsp;구조를&amp;nbsp;무너뜨렸던&amp;nbsp;순간은&amp;nbsp;아직도&amp;nbsp;선명합니다.&lt;br /&gt;그때의&amp;nbsp;아쉬움은&amp;nbsp;단순한&amp;nbsp;불합격이&amp;nbsp;아니라,&amp;nbsp;배운&amp;nbsp;방향을&amp;nbsp;스스로&amp;nbsp;놓쳐버렸다는&amp;nbsp;데서&amp;nbsp;왔습니다.&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그 이후 저는 제 성장 방식 자체를 다시 견고하게 정리하며 확립했습니다.&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;저의 &lt;b&gt;단기, 장기 목표는 무엇이고, 최종 꿈은 무엇인지, 제가 진정 무얼 위해 해당 활동을 하는지에 대해 하나씩 정리해 보았고, 주기적으로 메타인지와 회고를 통해 저 자신을 되돌아보는 시간&lt;/b&gt;을 가졌습니다.&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;당시 &amp;ldquo;&lt;b&gt;기능 요구 사항에 기재되지 않은 내용은 스스로 판단하여 구현한다.&lt;/b&gt;&amp;rdquo;라는 문구가 저에게 족쇄로 다가왔다면 현재의 저에게는 날개를 달아주는 문구처럼 느껴졌습니다.&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;정답을 찾아라&amp;rdquo;가 아니라 미션은 마음껏 성장하는 놀이터로서 &amp;ldquo;&lt;b&gt;너의 사고를 제한하지 마!&amp;rdquo;&lt;/b&gt;로 다가와서 너무 즐겁게 몰입하며 성장할 수 있었습니다.&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;덕분에&amp;nbsp; 올해 8기 프리코스를 시작하며, 7기 활동을 하던&lt;b&gt; 당시 족쇄처럼 느껴졌던 &amp;ldquo;기능 요구 사항에 없는 내용은 스스로 판단하여 구현한다&amp;rdquo;라는 문장이 이번 8기에는 저에게 날개를 달아주는 문구&lt;/b&gt;처럼 다가왔습니다.누구도 모르는 숨어있는 정해진 정답을 찾는것 보다 , 저의 사고를 설계로 증명하는 과정으로 미션을 바라보게 되었기 때문입니다.&lt;br /&gt;이번&amp;nbsp;프리코스에서&amp;nbsp;저는&amp;nbsp;구현보다&amp;nbsp;먼저&amp;nbsp;도메인의&amp;nbsp;의미를&amp;nbsp;해석하고,&amp;nbsp;객체의&amp;nbsp;책임과&amp;nbsp;협력&amp;nbsp;관계를&amp;nbsp;고민하는&amp;nbsp;데&amp;nbsp;몰입했습니다.&lt;br /&gt;&lt;br /&gt;문자열&amp;nbsp;계산기에서&amp;nbsp;단순한&amp;nbsp;예외&amp;nbsp;처리&amp;nbsp;대신&amp;nbsp;입력의&amp;nbsp;의미&amp;nbsp;차이를&amp;nbsp;고민했고,&amp;nbsp;자동차&amp;nbsp;경주와&amp;nbsp;로또&amp;nbsp;미션에서는&amp;nbsp;검증의&amp;nbsp;책임,&amp;nbsp;메시지&amp;nbsp;중심&amp;nbsp;설계,&amp;nbsp;그리고&amp;nbsp;제거하는&amp;nbsp;리팩토링의&amp;nbsp;가치를&amp;nbsp;몸으로&amp;nbsp;배웠습니다.&amp;nbsp;작년에는&amp;nbsp;구조를&amp;nbsp;늘려야&amp;nbsp;안전하다고&amp;nbsp;느꼈다면,&amp;nbsp;올해의&amp;nbsp;저는&amp;nbsp;불필요한&amp;nbsp;구조가&amp;nbsp;오히려&amp;nbsp;불안을&amp;nbsp;만든다는&amp;nbsp;걸&amp;nbsp;느끼기도&amp;nbsp;했습니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;특히&amp;nbsp;오픈미션에서의&amp;nbsp;TDD&amp;nbsp;경험은&amp;nbsp;제&amp;nbsp;사고를&amp;nbsp;결정적으로&amp;nbsp;바꾸었습니다.&lt;/b&gt;&lt;br /&gt;테스트는 단순히 버그를 잡는 도구가 아니라 요구사항을 탐색하고, 설계를 이끌며, 리팩토링을 가능하게 하는 안전망이라는 사실을 직접 체득했습니다. 테스트를 먼저 작성하며 애매한 요구사항이 구체적인 규칙으로 드러났고, 살아있는 테스트 덕분에 구조 변경을 두려움 없이 시도할 수 있었습니다. 동시에 모든 테스트가 같은 가치를 가지지는 않으며, 상황에 맞는 균형이 중요하다는 것도 배웠습니다.&lt;br /&gt;저는&amp;nbsp;여전히&amp;nbsp;부족하지만,&amp;nbsp;흔들릴&amp;nbsp;때&amp;nbsp;왜&amp;nbsp;흔들리는지&amp;nbsp;설명할&amp;nbsp;수&amp;nbsp;있고&amp;nbsp;다시&amp;nbsp;방향을&amp;nbsp;잡을&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;사람이&amp;nbsp;되었습니다.&amp;nbsp;&lt;b&gt;작년의&amp;nbsp;저는&amp;nbsp;정답을&amp;nbsp;향해&amp;nbsp;달렸고,&amp;nbsp;올해의&amp;nbsp;저는&amp;nbsp;문제의&amp;nbsp;의미를&amp;nbsp;해석하고&amp;nbsp;그&amp;nbsp;의미를&amp;nbsp;코드와&amp;nbsp;테스트로&amp;nbsp;책임&lt;/b&gt;지려&amp;nbsp;했습니다.&amp;nbsp;이것이&amp;nbsp;제가&amp;nbsp;이번&amp;nbsp;프리코스를&amp;nbsp;통해&amp;nbsp;얻은&amp;nbsp;가장&amp;nbsp;큰&amp;nbsp;성장이라고&amp;nbsp;생각합니다.&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이러한 이유로 저는 &lt;b&gt;이번 최종 코딩테스트에서 &quot;TDD로 미션을 진행하며 TDD 사이클을 통해 리팩토링&quot; 하는 도전을 선택&lt;/b&gt;하게 되었습니다.&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1. 리팩터링: 작동은 그대로 유지하면서 코드 품질을 높이는 방향&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2. 기능 확장: 기본 기능 위에 새로운 기능을 추가하는 방향&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;두 방향성이 있었지만 이번 8기 프리코스를 진행하며 특히나 저에게 큰 감명을 주었던 TDD를 활용할수 있는 &quot;리팩터링&quot;을 도전하게 되었습니다.&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;저는 분명 오픈미션을 진행하며 총 4번의 회고에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;&quot;TDD는 너무 좋은 도구지만 시간이 생각보다 많이 걸리니, 기간이 짧은 경우는 많은 고민을 하고 도입해야겠다&quot;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;라는 생각을 했었습니다.&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 최종 미션 가이드에 대한 안내와 문서를 읽으며 결정을 내렸습니다.&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;[최종 코딩 테스트의 목적] -&amp;nbsp;&lt;span style=&quot;color: #1f2328; text-align: start;&quot;&gt;&quot;프리코스에서는 3가지 기본 미션과 오픈 미션을 통해 주어진 요구 사항의 구현을 넘어 본인만의 도전을 함께 수행하였습니다. 최종 코딩 테스트는 이 경험을 본인의 힘으로 해냈음을 증명하는 시간입니다.&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&quot;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 근거없는 충동이 아니라, 1분간 명상을 하며 &lt;b&gt;&quot;7기 최종 탈락 후 노력한 저의 1년과 올해 8기 프리코스에, 오픈미션에 몰입한 저 자신&quot;을 떠올리며 결심&lt;/b&gt;을 할 수 있었습니다.&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그 후 미션을 진행하며 &lt;b&gt;TDD 사이클을 끝까지 지키기 위해 노력&lt;/b&gt;을 했습니다. 하지만 최종 코딩 테스트 진행하며 오픈미션에서 느꼈다 싶이, OOP와는 약간의 방향성이 달라짐을 느꼈습니다. 오픈미션의 시간이 아니었더라면 이런 느낌을 느꼈을 때 바로 TDD를 포기했었을 것이지만 전 이미 오픈 미션을 통해 2권의 도서와 4번의 실제 프로젝트 적용과 회고를 통해 충분히 인지를 하고 있어 크게 두렵지 않았습니다.&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;또한 &lt;b&gt;변경된 요구사항에 대해 '프리코스를 통해 얻은 교훈인 [요구사항 분석 도구로서의 TDD]'를 적용해보며 다시금 그 효과를 느낄&lt;/b&gt; 수있었습니다.&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;또한 모든 기능 구현을 마친 뒤 , 제공된 테스트와 실제 콘솔 출력을 해보려던차 출력 메서드를 수정할 수 없고, 정해진 메서드를 사용해야 했음을 다시금 발견했습니다.&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 &lt;b&gt;&quot;TDD의 리팩토링 안전망으로서의 TDD&quot;덕분에 큰 정신적 충격없이 빠르게 대처&lt;/b&gt;할 수 있었습니다.&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;마지막으로&amp;nbsp;최종&amp;nbsp;코딩테스트를&amp;nbsp;모두&amp;nbsp;마치고&amp;nbsp;제출&amp;nbsp;버튼을&amp;nbsp;누르는&amp;nbsp;순간,&amp;nbsp;제&amp;nbsp;마음은&amp;nbsp;조금&amp;nbsp;이상했습니다.&lt;br /&gt;긴장감이&amp;nbsp;완전히&amp;nbsp;가시기보다는,&amp;nbsp;그보다&amp;nbsp;먼저&amp;nbsp;&amp;ldquo;여기까지&amp;nbsp;정말&amp;nbsp;열심히&amp;nbsp;왔다&amp;rdquo;는&amp;nbsp;감각이&amp;nbsp;더&amp;nbsp;크게&amp;nbsp;남았습니다.&lt;br /&gt;기출&amp;nbsp;문제들을&amp;nbsp;반복해서&amp;nbsp;풀며&amp;nbsp;준비했던&amp;nbsp;시간들이&amp;nbsp;스쳐&amp;nbsp;지나갔고,&amp;nbsp;단순히&amp;nbsp;문제를&amp;nbsp;맞히는&amp;nbsp;연습이&amp;nbsp;아니라&amp;nbsp;제&amp;nbsp;사고를&amp;nbsp;정리하고&amp;nbsp;우선순위를&amp;nbsp;세우고&amp;nbsp;설계하는&amp;nbsp;모습들이&amp;nbsp;떠올랐습니다.&lt;br /&gt;&lt;b&gt;작년에는&amp;nbsp;문제를&amp;nbsp;마주하면&amp;nbsp;조급함이&amp;nbsp;먼저&amp;nbsp;튀어나왔는데,&amp;nbsp;이번에는&amp;nbsp;요구사항을&amp;nbsp;정확히&amp;nbsp;이해하고,&amp;nbsp;작은&amp;nbsp;단위로&amp;nbsp;확인하며,제가&amp;nbsp;선택한&amp;nbsp;설계와&amp;nbsp;구현을&amp;nbsp;꼼꼼히&amp;nbsp;검증하고,&amp;nbsp;시간&amp;nbsp;안에&amp;nbsp;무엇을&amp;nbsp;선택하고&amp;nbsp;무엇을&amp;nbsp;포기할지까지&amp;nbsp;판단&lt;/b&gt;하려는&amp;nbsp;방식이&amp;nbsp;자연스럽게&amp;nbsp;나오고&amp;nbsp;있음을&amp;nbsp;느꼈습니다.&lt;br /&gt;무엇보다&amp;nbsp;작년에&amp;nbsp;비해&amp;nbsp;스스로가&amp;nbsp;많이&amp;nbsp;성장해&amp;nbsp;있다는&amp;nbsp;감각이&amp;nbsp;분명히&amp;nbsp;들었습니다.&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;물론&amp;nbsp;시간&amp;nbsp;제한&amp;nbsp;속에서&amp;nbsp;더&amp;nbsp;개선하고&amp;nbsp;싶었던&amp;nbsp;부분들은&amp;nbsp;분명히&amp;nbsp;많이&amp;nbsp;남아&amp;nbsp;있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;AppConfig도입, 방어적 복사와 불변성을 꼼꼼히 모두 챙기지 못한것, 에러메시지 Enum을 통한 관리, 파싱 클래스 도입 후 책임 분배, 일반화 로직으로 깔끔하게 메서드 및 코드 로직 정리, 빈약한 책임주도 설계, 풍부한 VO 및 도메인 도입, DTO 도입 등등 너무 많이 개선하고 싶었습니다.&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;제출&amp;nbsp;직전까지도&amp;nbsp;&amp;ldquo;여기를&amp;nbsp;이렇게&amp;nbsp;바꾸고&amp;nbsp;싶다....&amp;rdquo;라는&amp;nbsp;생각이&amp;nbsp;계속&amp;nbsp;들었고,&amp;nbsp;다&amp;nbsp;하지&amp;nbsp;못했다는&amp;nbsp;아쉬움도&amp;nbsp;남았습니다.&lt;br /&gt;그럼에도&amp;nbsp;예전과&amp;nbsp;다른&amp;nbsp;점은,&amp;nbsp;&lt;b&gt;그&amp;nbsp;아쉬움이&amp;nbsp;후회로만&amp;nbsp;남지&amp;nbsp;않는다는&amp;nbsp;것&lt;/b&gt;입니다.&lt;br /&gt;이미&amp;nbsp;머릿속에서는&amp;nbsp;내일&amp;nbsp;새로&amp;nbsp;브랜치를&amp;nbsp;파서&amp;nbsp;코드를&amp;nbsp;다시&amp;nbsp;열어보고,&amp;nbsp;온전히&amp;nbsp;리팩토링하며&amp;nbsp;더&amp;nbsp;좋은&amp;nbsp;코드로&amp;nbsp;다듬는&amp;nbsp;장면을&amp;nbsp;떠올리고&amp;nbsp;있습니다.&lt;br /&gt;그런&amp;nbsp;시간을&amp;nbsp;생각하면&amp;nbsp;묘하게&amp;nbsp;설레고&amp;nbsp;신나며,&amp;nbsp;오히려&amp;nbsp;즐거운&amp;nbsp;마음이&amp;nbsp;듭니다.&lt;br /&gt;&lt;b&gt;어쩌면&amp;nbsp;저는&amp;nbsp;결과보다도&amp;nbsp;이런 성장하는 과정을&amp;nbsp;가장&amp;nbsp;좋아하는&amp;nbsp;사람인지도&amp;nbsp;모르겠습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;저를&amp;nbsp;이렇게&amp;nbsp;성장시켜주시고,&amp;nbsp;이&amp;nbsp;과정&amp;nbsp;자체를&amp;nbsp;즐길&amp;nbsp;수&amp;nbsp;있게&amp;nbsp;해준&amp;nbsp;우아한테크코스&amp;nbsp;운영진분들에게&amp;nbsp;감사하다는&amp;nbsp;말을&amp;nbsp;꼭&amp;nbsp;남기고&amp;nbsp;싶습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;정말 많이 감사합니다 :)&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;최종합격&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1132&quot; data-origin-height=&quot;902&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWrV1P/dJMcad1RmcT/atHcCblANWzqd7Kr21qzUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWrV1P/dJMcad1RmcT/atHcCblANWzqd7Kr21qzUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWrV1P/dJMcad1RmcT/atHcCblANWzqd7Kr21qzUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWrV1P%2FdJMcad1RmcT%2FatHcCblANWzqd7Kr21qzUK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1132&quot; height=&quot;902&quot; data-origin-width=&quot;1132&quot; data-origin-height=&quot;902&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;드디어 !! 최종 합격 소식을 전달받게 되었다 !&lt;br /&gt;새해 선물로 너무 가지고 싶었던 선물을 받게되어 너무 가슴이 벅차고 행복하다...&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1월까지는 푸욱 쉬고 다시 1년간의 여정에 대한 계획을 세워 봐야겠다...!&lt;/p&gt;</description>
      <category>외부활동/우아한테크코스 [프리코스]</category>
      <author>softmoca__</author>
      <guid isPermaLink="true">https://softmoca.tistory.com/619</guid>
      <comments>https://softmoca.tistory.com/619#entry619comment</comments>
      <pubDate>Thu, 15 Jan 2026 15:26:38 +0900</pubDate>
    </item>
    <item>
      <title>[우아한테크코스 8기] 프리코스 오픈미션 최종회고</title>
      <link>https://softmoca.tistory.com/568</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;시작하며&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6주간 진행한 프리코스의 마지막 미션을 3주간 TDD라는 주제에 대해 목표를 잡고 도전하며 엄청난 몰입으로 많은 성장을 할수 있어 너무 행복했다. 프리코스 여러 미션들 그중에서 이번 오픈미션이 아니었다면 나는 아마 TDD에 대해 현재와 같은 경험을 얻지 못하며 주먹구구식으로 TDD를 접하고 진정한 가치들을 미처 들여다보지 못한 채 넘겼을 것 같다.&lt;br /&gt;프리코스를 통해 TDD에 대한 궁금증과 관심이 생겼고 그 덕분에 2권의 TDD 도서를 정독하고 나만의 언어로 정리하며 다시금 소중한 지난 프리코스 미션들을 TDD로 다시 돌아보며 많은 시야와 깨달음을 얻을 수 있었다.&lt;br /&gt;&lt;br /&gt;오픈 미션을 진행하며 늘 하루 10시간 이상 오픈 미션을 위해 끙끙대며 기상 직후, 식사 도중, 운동 도중, 대중 교통 속 제 모든 일상이 오픈미션에 대한 생각으로 가득 차 있는걸 종종 인지하며 스스로 너무 대견하고 온전한 몰입 그자체로 너무 행복 했다.&lt;br /&gt;그간 프리코스 미션들과 마찬가지로 여느 코딩테스트 시험과 달리&amp;nbsp;&amp;nbsp;&amp;ldquo;정답을 찾는 것&amp;rdquo;이 아닌 &amp;ldquo;성장을 경험하는 과정&amp;rdquo;들을 매번 기록하고 회고하는 과정들이 너무 아름다웠다.&lt;br /&gt;우아한테크코스 운영진분들 덕분에 무료로 너무 좋은 교육 프로그램을 통해 즐기며 폭발적인 성장과 너무 아름다운 감정들을 느낄 수 있는 기회를 주셔서 늘 너무 감사한 마음이 든다&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&amp;ldquo;선정&amp;nbsp;배경-&amp;nbsp;왜&amp;nbsp;TDD&amp;nbsp;였는가,&amp;nbsp;반복되는&amp;nbsp;실패와&amp;nbsp;고민에서&amp;nbsp;찾은&amp;nbsp;질문&amp;rdquo;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그간 프리코스 미션들을 진행하며 테스트는 &amp;ldquo;정답을 검증 하고 버그를 잡을수 있는 도구&amp;rdquo;라고 생각하며 테스트 코드를 작성했었다. 하지만 3주차 미션에서 보너스 번호 매칭 로직 버그를 제출 전날 발견하였고, 디버깅을 하며 원인 파악과 수정에 많은 시간이 소요되었다.&lt;br /&gt;아주 높은 몰입을 통해 미션을 진행하며 최대한 테스트 코드를 잘 작성하기 위해 노력을 했지만 뒤늦게 버그를 발견해서 많이 놀랐었다. 이후 회고를 진행하며, 내가 집중을 하지 않았나 ? 테스트 코드를 꼼꼼히 작성하지 않았나 ? 요구 사항을 꼼꼼히 확인하지 않았나 ? 등등 확인하는 시간을 가졌다.&lt;br /&gt;그 과정에서 요구 사항 분석 및 이해와 꼼꼼한 테스트코드 작성 보다 압도적으로&amp;nbsp;&amp;nbsp;&amp;lsquo;객체지향적인 설계&amp;rsquo;에만 많은 고민을 했었던 것이 원인이었다는 결론에 도달했다.&lt;br /&gt;객체지향적인 설계를 고민하고 이에 대한 학습을 하는 과정에서 분명 엄청난 성장을 했었지만, 가장 기본인 &amp;lsquo;문제 없이 돌아가야 한다&amp;rsquo;는 소프트웨어의 본질을 잠시 놓치고 있었다.&lt;br /&gt;이 경험을 통해 설계의 아름다움 이전에,요구사항을 정확히 이해하고, 그 요구사항을 끝까지 책임지는 테스트가 먼저라는 사실을 다시 한 번 깊게 깨닫게 되었다.&lt;br /&gt;&lt;br /&gt;또한 2주차 미션 과정 중 Service 계층을 만들었다가 제거 하는 리팩토링과정에서&amp;nbsp;&amp;nbsp;&quot;이 코드를 지워도 괜찮을까?&quot; &quot;이렇게 바꾸면 다른 곳이 깨지지 않을까?&quot; 와 같은 많은 고민들과 함께 상당한 혼란스러움을 경험했다.&lt;br /&gt;테스트 코드가 있었지만, 구현 후에 작성한 테스트였기에 구조 변경 시 함께 무너졌다.&lt;br /&gt;테스트를 수정하고 또 깨지는 악순환 속에서, 결국 리팩토링을 중도에 포기하거나 최소한으로만 진행했었다.&lt;br /&gt;또한 1~3주차 매 미션 마다 여러 설계 방향에 대한 고민이 끊이지 않았다. &quot;검증 로직은 어디에 두어야 하나?&quot;, &amp;ldquo;콜백 패턴과 전략 패턴을 적용해야 하나?&amp;rdquo;, &amp;ldquo;출력의 책임과 DTO는 ?&amp;ldquo;, &amp;ldquo;이건 단위 테스트인가? 통합테스트인가 ?&amp;rdquo;, &quot;값 객체로 만들어야 하나, 원시값 포장으로 충분한가?&quot;, &quot;Stream API를 사용하려면 getter를 추가해야 하는데, 이게 맞나?&quot; 구현을 먼저 하다 보니 &quot;이미 만들어진 구조에 맞춰 테스트를 억지로 끼워 맞추는&quot; 상황이 반복되었다.&lt;br /&gt;테스트 작성이 어렵다는 것은 설계가 좋지 않다는 신호였지만, 지금 생각해보니 그저 학습과정이라서 힘든것이라 이런 신호를 미처 인식하지 못던것 같다.&lt;br /&gt;&lt;br /&gt;무엇보다&amp;nbsp;&amp;nbsp;지난 7기 프리코스 4주차 편의점 미션의 회고 과정도 떠올랐다.&lt;br /&gt;당시에도 최종 제출 시 1개의 테스트가 실패하여, 디버깅을 하며 원인 파악과 수많은 수정을 시도했다.&amp;nbsp;&lt;br /&gt;단 하나의 실패한 테스트를 해결하기 위해 모든 코드를 뒤엎으며, 기존에 지켜왔던 프로그래밍 요구사항들을 모두 무너뜨리고 말았다.&lt;br /&gt;&lt;br /&gt;이런 경험들은 나에게 중요한 질문을 남겼다.&lt;br /&gt;&amp;nbsp;&quot;왜&amp;nbsp;테스트를&amp;nbsp;작성했는데도&amp;nbsp;이런&amp;nbsp;일이&amp;nbsp;반복되는&amp;nbsp;걸까?&quot;&lt;br /&gt;&amp;nbsp;&quot;테스트&amp;nbsp;코드가&amp;nbsp;있으면&amp;nbsp;안전할&amp;nbsp;줄&amp;nbsp;알았는데,&amp;nbsp;왜&amp;nbsp;리팩토링할&amp;nbsp;때&amp;nbsp;무너졌을까?&quot;&amp;nbsp;&lt;br /&gt;그리고 테스트 작성 시기의 중요성에 대해 깨달게 되었다.&lt;br /&gt;구현을 먼저 하고 테스트를 나중에 작성했기에 요구사항 누락을 뒤늦게 발견했고, 구현에 의존적인 테스트가 되어 리팩토링 시 깨졌으며, 테스트가 설계를 개선하기보다 구현을 정당화하는 도구가 되어있었다.&lt;br /&gt;즉,&amp;nbsp;&amp;nbsp;테스트는 단순히 정답을 검증하는 수단이 아니라, 요구사항을 발견하고 설계를 이끄는 도구임을 인식하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;ldquo;&lt;b&gt;목표&amp;nbsp;-&amp;nbsp;오해를&amp;nbsp;깨고&amp;nbsp;진실을&amp;nbsp;체득하기&amp;rdquo;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 TDD를 &amp;ldquo;한 번 적용해 보는 경험&amp;rdquo;에서 끝내는 것이 아니라, 프리코스를 진행하며 내가 테스트 코드에 대해 가지고 있던 여러 오해들을 바로잡고, 실제 코드와 시행착오 속에서 설계와 테스트의 진짜 가치를 몸으로 체득하는 것이 이번 오픈 미션의 가장 큰 목표였다.&lt;/p&gt;
&lt;p data-end=&quot;549&quot; data-start=&quot;261&quot; data-ke-size=&quot;size16&quot;&gt;첫 번째 목표는 요구사항 탐색 도구로서의 TDD를 체험하는 것이었다.&lt;br /&gt;Red &amp;rarr; Green &amp;rarr; Refactor 사이클을 반복하며, 테스트를 먼저 작성하는 과정에서 자연스럽게 &amp;ldquo;이 기능은 어떤 경우까지 책임져야 하지?&amp;rdquo;, &amp;ldquo;이건 예외 상황인가, 정상 흐름인가?&amp;rdquo; 같은 질문을 스스로에게 던지도록 만들고 싶었다.&lt;br /&gt;특히 구현 전에 실패하는 테스트를 먼저 작성함으로써, 평소라면 지나쳤을 예외 케이스를 미리 발견하고, 작은 단위로 검증을 쌓아가며 큰 실수를 사전에 차단하는 경험을 직접 느끼고자 했다.&lt;/p&gt;
&lt;p data-end=&quot;760&quot; data-start=&quot;551&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;760&quot; data-start=&quot;551&quot; data-ke-size=&quot;size16&quot;&gt;두 번째 목표는 리팩토링 안전망으로서의 TDD를 체험하는 것이었다.&lt;br /&gt;그동안의 프리코스에서는 &amp;ldquo;구현 &amp;rarr; 테스트&amp;rdquo; 순서로 진행하다 보니, 리팩토링을 시도할 때마다 &amp;ldquo;이 코드를 지워도 괜찮을까?&amp;rdquo;, &amp;ldquo;여길 바꾸면 어디가 또 깨질까?&amp;rdquo;라는 불안이 항상 따라다녔다.&lt;br /&gt;이번에는 테스트를 먼저 두고 구현을 쌓아 올린 뒤, TDD 사이클 안에서 구조를 과감하게 고쳐 보며 &lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&amp;ldquo;이렇게 바꿔도 될까?&amp;rdquo; &amp;rarr; &amp;ldquo;테스트 통과! 안전하다.&amp;rdquo; &lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;라는 흐름을 실제로 체감해 보고 싶었다. 테스트가 더 이상 &lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;수정의 족쇄&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;가 아니라, &lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;구조 변경을 뒷받침해 주는 안전망&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;이 되는지 확인하고, 그 덕분에 리팩토링을 회피하지 않고 오히려 적극적으로 시도할 수 있는지 검증해 보고자 했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;1113&quot; data-start=&quot;934&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1113&quot; data-start=&quot;934&quot; data-ke-size=&quot;size16&quot;&gt;세 번째 목표는 설계 고민을 TDD로 풀어보는 것이었다.&lt;br /&gt;그동안 &amp;ldquo;검증 로직은 어디에 둘까?&amp;rdquo;, &amp;ldquo;DTO 변환은 어디 책임이지?&amp;rdquo;, &amp;ldquo;이건 VO로 빼야 하나?&amp;rdquo; 같은 설계 고민들을 머릿속에서만 굴리다가, 이미 만들어진 구조에 맞춰 테스트를 억지로 끼워 넣는 경우가 많았다.&lt;br /&gt;이번 오픈 미션에서는 반대로, &lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&amp;ldquo;이 객체를 실제로 어떻게 사용하게 될까?&amp;rdquo; &lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;를 먼저 고민하며 테스트를 작성하고, 그 테스트가 자연스럽게 설계를 이끌어 가는 경험을 목표로 삼았다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;1333&quot; data-start=&quot;1114&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1333&quot; data-start=&quot;1116&quot; data-ke-size=&quot;size16&quot;&gt;테스트하기 어려운 코드를 설계의 냄새로 받아들이고, &amp;ldquo;기존 억지 테스트&amp;rdquo;가 아니라 &amp;ldquo;정말 필요하고 자연스러운 테스트&amp;rdquo;가 무엇인지 구분해 보며, 테스트하기 쉬운 코드 = 좋은 설계라는 말을 내 코드 기준으로 검증해 보고자 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;1635&quot; data-start=&quot;1335&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;1635&quot; data-start=&quot;1335&quot; data-ke-size=&quot;size16&quot;&gt;마지막 네 번째 목표는 매 미션마다 TDD 사고 과정과 경험들을 기록으로 남기는 것이었다.&lt;br /&gt;TDD가 실제로 어떤 순간에 도움을 주었는지, 어떤 선택에서 갈등했고 왜 그 방향을 택했는지, 아쉬운 점이 있다면 무엇이었는지를 매번 정리하면서, 단순한 결과물이 아니라 사고 과정 전체를 학습 자산으로 축적하고 싶었다.&lt;br /&gt;특히 &amp;ldquo;잘했다 못했다&amp;rdquo;의 평가보다는, &amp;ldquo;무엇을 느꼈는지&amp;rdquo;, &amp;ldquo;다음에는 어떻게 할지&amp;rdquo;에 집중해 회고함으로써, 이번 오픈 미션이 끝나도 계속해서 되돌아볼 수 있는 나만의 TDD 학습 기록을 남기고자 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;실행계획&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 세운 나의 계획은 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1주차&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;3일간 &amp;lsquo;테스트주도 개발 시작하기&amp;rsquo; 도서 정독 및 정리&lt;/li&gt;
&lt;li&gt;테스트주도 개발 시작하기 도서 정리 후 나만의 언어로 TDD에 대해 정리하기&lt;/li&gt;
&lt;li&gt;3일간 1주차 문자열 계산기 미션 TDD로 다시 진행&lt;/li&gt;
&lt;li&gt;1주차 문자열 계산기 미션 TDD로 진행하며 기록한 목표 4 경험들 정리&lt;/li&gt;
&lt;li&gt;3일간 2주차 자동차 경주 미션 TDD로 다시 진행&lt;/li&gt;
&lt;li&gt;2주차 자동차 경주 미션 TDD로 진행하며 기록한 목표 4 경험들 정리&lt;/li&gt;
&lt;li&gt;1주차 회고 및 계획 변동사항 확인&lt;/li&gt;
&lt;li&gt;추가 참고 도서 &amp;lsquo;테스트 주도개발 TDD 실천법과 도구 정리&amp;rsquo; 볼 지 고려&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2주차&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;3일간 2주차 로또 미션 TDD로 다시 진행&lt;/li&gt;
&lt;li&gt;3주차 로또 미션 TDD로 진행하며 기록한 목표 4 경험들 정리&lt;/li&gt;
&lt;li&gt;3일간 7기 4주차 편의점 미션 TDD로 다시 진행&lt;/li&gt;
&lt;li&gt;4주차 자동차 경주 미션 TDD로 진행하며 기록한 목표 4 경험들 정리&lt;/li&gt;
&lt;li&gt;2주차 회고 및 진행 사항 확인 및 계획 변동&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 코드 리뷰를 진행하며 기본적으로 TDD는 시간이 오래 걸린다는 것을 알고 있어 너무 타이트하게 계획을 세우진 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 점을 미리 고려한점이 지금와서 보니 정말 신의 한수 였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비교적 간단한 1,2주차 미션의 경우에도 기존 계획보다 훨씬 오래 걸렸으며, 무엇보다 2차에 너무 많이 방황해서 이미 계획이 딜레이 되었지만 시간이 남을 경우 보려고 한 도서를 읽어 본 점이 꽤나 치명적이었다. 하지만 너무나 필요한 과정이었고 이러한 과정들이 내가 그저 대충 미션을 대하지 않았다는 증거이기도해서 뿌듯했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그간 프리코스 코드 리뷰를 하며 많은분들이 TDD는 경험 한 뒤 시간이 너무 오래걸리고 현실적으로 자신과는 맞지 않아 앞으로는 쓰지 않을 것이라는 결론을 지은것 봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아마 나 또한 단 1주의 시간동안 하나의 미션에 TDD를 적용하며 접했다면 같은 결론에 도달했을 것 같다. 하지만 그간 나의 프리코스 기간동안의 경험들과&amp;nbsp; 3주간 2권의 도서와 4번의 미션들에 TDD를 적용을 거친 나는 전혀 다른 결론에 도달했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나에게는 정말 신세계이며 이런게 패러다임인가 싶은 느낌이다. 특히 최대한 노력을 했지만 놓치며 아쉬워하는 경험들이 이런 방법론과 시스템으로 보완이 된다는 점이 정말 쇼킹 그자체다 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 늘 그랬듯 앞으로의 개발인생을 살며 언제든 이러한 나의 기치관이 달라질 수 있을것 같다. 하지만 현재의 나는 너무 좋은 무기와 장비를 얻게되어 그저 행복할 뿐이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&amp;ldquo;요구사항 탐색 도구로서의 TDD&amp;rdquo;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오픈 미션을 진행하며 가장 인상 깊은 경험은 요구 사항 탬색 도구로서의 TDD의 힘이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열 계산기 미션을 TDD로 다시 진행하며 첫 번째 목표를 바로 체험할 수 있었다.&lt;br /&gt;단위 테스트는 모두 통과했지만 ApplicationTest가 실패했을 때, 원인을 파악하며 초기 요구사항을 제대로 파악하지 못했음을 발견했다. Java 리터럴의 개행문자와 콘솔 입력의 역슬래시+n의 차이를 대충 이해하고 있었던 것이 문제였다. 이미 3번째 접하는 미션이었지만 매번 같은 문제에 대해 조금이 나마 더 깊은 이해를 가질수 있다는 점이 너무 신기했고, TDD를 접해서 순간 너무너무 행복했다.&lt;br /&gt;이 경험을 통해 TDD 사이클을 반복하는 과정 자체가 요구사항을 놓치지 않도록 계속해서 되묻게 만든다는 것을 느꼈다. 단순히 &quot;읽고 이해하는 것&quot;이 아니라 시스템적으로, 강제적으로 이해하게 만드는 것이었다.&lt;br /&gt;로또 미션에서는 Rank 설계 과정에서 보너스 번호 로직의 숨은 버그를 사전에 발견했다. 처음에는 &quot;보너스가 없으면 탈락&quot;이라는 잘못된 가정을 하고 구현하려 했지만, 테스트를 통해 &quot;1등, 3등, 4등, 5등은 보너스와 무관하게 결정된다&quot;는 실제 요구사항을 정확히 이해하게 되었다. 3주차에서 겪었던 동일한 유형의 버그를 이번에는 사전에 차단할 수 있었다.&lt;br /&gt;지난 3주차 미션을 진행할 때도 분명 가능한 온힘을 다해 최대한 집중하고 몰입을 했었지만, 내가 짠 테스트 코드가 부실했다는 사실에 꽤나 많이 나 자신에게 실망을 했었다.&amp;nbsp; 하지만 TDD를 통해 시스템적으로 반강제적으로 테스트 코드, 나아가 리팩토링을 통한 프로덕션 코드 자체가 자연스럽게&amp;nbsp;&amp;nbsp;스트레스 없이 단단해 질 수 있다는 점이 너무 충격적이었고, 너무 신나게 오픈미션을 진행할수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;편의점 미션에서는 프로모션 계산 로직의 복잡한 케이스들을 테스트 작성 과정에서 사전에 발견할 수 있었다. &quot;2+1 프로모션이면 3개 구매 시 1개 무료&quot;는 명확했지만, &quot;5개 구매하면?&quot;이라는 질문에서 막혔다. 테스트를 먼저 작성하며 고민하니, 5개 구매 시에는 3개만 프로모션이 적용되고 2개는 정가라는 &quot;세트 단위 계산&quot; 규칙을 발견했다. 프로모션 재고가 7개인데 10개를 구매하면 어떻게 되는지, 멤버십 할인은 프로모션 적용 금액에도 적용되는지 등 숨어 있던 요구사항들이 테스트 작성 과정에서 하나씩 드러났다.&lt;br /&gt;&quot;이렇게 동작하겠지&quot;라는 추측이 아니라 &quot;실제로 확인해보자&quot;는 태도가 필요하다는 것을 깨달은 점이 가장 인상 깊었다. TDD는 막연한 가정을 줄이고, 확실한 검증 위에서 구현하도록 훈련시키는 도구였다.&lt;br /&gt;작년에 놓쳤던 마지막 1개의 테스트를 다시 확인해 볼순 없지만 이렇게나 꼼꼼히 요구사항을 정확히 분석할 수 있다는 점에서 TDD의 힘에 너무 놀랐다. 그저 태생적으로의 꼼꼼함과 집중이 아닌 개발론적으로 이렇게나 요구사항을 꼼꼼히 이해하고 개발할수 있다는 점이 너무신기했다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&amp;ldquo;마치며&amp;nbsp;-&amp;nbsp;오해를&amp;nbsp;깨고&amp;nbsp;진실을&amp;nbsp;체득했는지&amp;nbsp;되돌아보기&amp;rdquo;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요구사항 탐색 도구로서의 TDD는 확실히 체험했다. 테스트를 먼저 작성하며 요구사항을 자연스럽게 탐색하고, 누락된 로직을 사전에 발견하는 경험을 했다. 3주차에서 겪었던 보너스 번호 버그와 같은 실수를 이번에는 사전에 차단할 수 있었다.&lt;br /&gt;리팩토링 안전망으로서의 TDD도 충분히 경험했다. &quot;구현 후 테스트&quot;와 &quot;테스트 후 구현&quot;의 차이를 몸소 느꼈고, 테스트가 수정의 족쇄가 아닌 구조 변경의 안전망이 된다는 것을 확인했다. 무엇보다 심리적 안정감이 주는 효과가 생각보다 훨씬 커서 스트레스가 비교적 훨씬 적은 상태로 리팩토링을 고민하는 점이 너무 충격적이었다.&lt;br /&gt;설계 피드백 도구로서의 TDD 역시 체감했다. &quot;어떻게 사용할 것인가&quot;를 먼저 고민하며 테스트를 작성하니 설계가 자연스럽게 도출되었고, 테스트하기 어려운 코드는 설계 문제의 신호라는 것을 직접 경험했다.&lt;br /&gt;예상치 못한 발견도 있었다. 모든 테스트가 같은 가치를 가지지 않는다는 것, TDD와 OOP 학습은 때로 충돌할 수 있다는 것, 완벽한 설계는 없으며 상황에 맞는 균형점을 찾는 것이 중요하다는 것을 배웠다.&lt;br /&gt;&lt;br /&gt;3주차 소감문에서 &quot;테스트를 먼저 작성하면 인터페이스 설계부터 하게 되어 사용하기 편한 API가 자연스럽게 나올 것 같다&quot;, &quot;예외 상황과 버그를 미리 필요한 시점에 고민하며 더 견고한 코드를 작성할 수 있을 것 같다&quot;고도 작성을 했었는데 실제로 이러한 장점을 온전히 느끼고 경험했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무엇보다 중요한 것은, 이 모든 깨달음이 이론이 아니라 실제 코드를 작성하며 겪은 경험에서 나왔다는 점이다. 책에서 읽은 내용은 쉽게 잊히지만, 직접 좌절하고 해결하며 얻은 깨달음은 오래 남는 다는걸 알아 더욱 뿌듯했다.&lt;br /&gt;프리코스 전체를 돌아보면, 1주차는 객체지향적 사고의 시작이었고, 2주차는 설계와 테스트의 본질을 탐구하는 시간이었으며, 3주차는 제거하는 리팩토링과 개념의 본질을 이해하는 시간이었다. 그리고 오픈미션은 그동안의 모든 고민과 실패를 TDD라는 렌즈로 다시 바라보며 통합하는 시간이었다.&lt;br /&gt;&lt;br /&gt;정확히 1년 전 7기 프리코스에서는 예외 처리와 히든 케이스를 찾는 것에만 집중했었다. 지금의 나는 설계 원칙과 패턴, 테스트 전략을 고민하고, 더 나아가 TDD를 통해 요구사항과 설계를 탐색하는 방법을 배웠다. 이 변화가 나에게는 가장 큰 성장의 증거이자 앞으로 달려갈 수 있는 큰 동력이다.&lt;br /&gt;&lt;br /&gt;마지막으로 너무 좋은 교육 프로그램을 무상으로 제공해주시고, 이렇게 성장할 수 있는 기회를 주셔서 정말 감사합니다 운영진 선생님분들. 프리코스가 끝나더라도 이번에 체득한 TDD의 가치를 잊지 않고, 앞으로의 개발 여정에서 계속 실천하겠습니다 !&lt;br /&gt;본&amp;nbsp;교육&amp;nbsp;과정에서의&amp;nbsp;페어프로그래밍을&amp;nbsp;통해&amp;nbsp;TDD를&amp;nbsp;하는&amp;nbsp;그&amp;nbsp;날을&amp;nbsp;고대하며,&amp;nbsp;최종&amp;nbsp;코딩테스트를&amp;nbsp;위해&amp;nbsp;노력&amp;nbsp;하고&amp;nbsp;있겠습니다.&amp;nbsp;다시&amp;nbsp;한번&amp;nbsp;감사합니다&amp;nbsp;:)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>외부활동/우아한테크코스 [프리코스]</category>
      <author>softmoca__</author>
      <guid isPermaLink="true">https://softmoca.tistory.com/568</guid>
      <comments>https://softmoca.tistory.com/568#entry568comment</comments>
      <pubDate>Wed, 26 Nov 2025 22:04:34 +0900</pubDate>
    </item>
    <item>
      <title>[우아한테크코스 8기] 프리코스 오픈미션 4차 회고- 편의점 TDD</title>
      <link>https://softmoca.tistory.com/564</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MSiVg/dJMcacatCz6/2cWy71MotClFEsasHdtMQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MSiVg/dJMcacatCz6/2cWy71MotClFEsasHdtMQk/img.png&quot; data-origin-width=&quot;840&quot; data-origin-height=&quot;611&quot; data-is-animation=&quot;false&quot; style=&quot;width: 48.2437%; margin-right: 10px;&quot; data-widthpercent=&quot;48.81&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MSiVg/dJMcacatCz6/2cWy71MotClFEsasHdtMQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMSiVg%2FdJMcacatCz6%2F2cWy71MotClFEsasHdtMQk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;840&quot; height=&quot;611&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r83Tg/dJMcacatCzS/3qZpYlSqCdUSIwTUXH8f71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r83Tg/dJMcacatCzS/3qZpYlSqCdUSIwTUXH8f71/img.png&quot; data-origin-width=&quot;656&quot; data-origin-height=&quot;455&quot; data-is-animation=&quot;false&quot; style=&quot;width: 50.5935%;&quot; data-widthpercent=&quot;51.19&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r83Tg/dJMcacatCzS/3qZpYlSqCdUSIwTUXH8f71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr83Tg%2FdJMcacatCzS%2F3qZpYlSqCdUSIwTUXH8f71%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;656&quot; height=&quot;455&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dxW6Xd/dJMcacatCzW/RMdx1tfRdPh3COwNzRGKbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dxW6Xd/dJMcacatCzW/RMdx1tfRdPh3COwNzRGKbk/img.png&quot; data-origin-width=&quot;796&quot; data-origin-height=&quot;548&quot; data-is-animation=&quot;false&quot; style=&quot;width: 51.6889%; margin-right: 10px;&quot; data-widthpercent=&quot;52.3&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dxW6Xd/dJMcacatCzW/RMdx1tfRdPh3COwNzRGKbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdxW6Xd%2FdJMcacatCzW%2FRMdx1tfRdPh3COwNzRGKbk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;796&quot; height=&quot;548&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LLb3o/dJMcagcS28G/qswwfyg8akL26DgsQtQut0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LLb3o/dJMcagcS28G/qswwfyg8akL26DgsQtQut0/img.png&quot; data-origin-width=&quot;791&quot; data-origin-height=&quot;597&quot; data-is-animation=&quot;false&quot; style=&quot;width: 47.1484%;&quot; data-widthpercent=&quot;47.7&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LLb3o/dJMcagcS28G/qswwfyg8akL26DgsQtQut0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLLb3o%2FdJMcagcS28G%2Fqswwfyg8akL26DgsQtQut0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;791&quot; height=&quot;597&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/softmoca/java-convenience-store-7/tree/softmoca-tdd&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/softmoca/java-convenience-store-7/tree/softmoca-tdd&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1764213833429&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - softmoca/java-convenience-store-7&quot; data-og-description=&quot;Contribute to softmoca/java-convenience-store-7 development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/softmoca/java-convenience-store-7/tree/softmoca-tdd&quot; data-og-url=&quot;https://github.com/softmoca/java-convenience-store-7&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cULqlH/hyZOMmeAhl/PjJgXteMdbNYCe9crrBLP1/img.png?width=1200&amp;amp;height=600&amp;amp;face=988_120_1036_172,https://scrap.kakaocdn.net/dn/b5CzvI/hyZONk8KIi/LIjvYpEEVM9VkGKxrsXkF0/img.png?width=1200&amp;amp;height=600&amp;amp;face=988_120_1036_172&quot;&gt;&lt;a href=&quot;https://github.com/softmoca/java-convenience-store-7/tree/softmoca-tdd&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/softmoca/java-convenience-store-7/tree/softmoca-tdd&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cULqlH/hyZOMmeAhl/PjJgXteMdbNYCe9crrBLP1/img.png?width=1200&amp;amp;height=600&amp;amp;face=988_120_1036_172,https://scrap.kakaocdn.net/dn/b5CzvI/hyZONk8KIi/LIjvYpEEVM9VkGKxrsXkF0/img.png?width=1200&amp;amp;height=600&amp;amp;face=988_120_1036_172');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - softmoca/java-convenience-store-7&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to softmoca/java-convenience-store-7 development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;시작하며&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드디어 마지막 편의점 미션을 TDD로 다시 경험해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요구사항 탐색 도구로서의 TDD를 활요하니 작년 그도록 어렵고 복잡했던 편의점 미션이 정말 놀라울 정도로 스무스하게 진행되었다. 물론 작년에 한번 경험했었기에 당연히 그보다 수월하게 느껴지는 것은 당연하겠지만 그런 사실을 감안하더라도 매 단계 점진적으로 진행을 할수록 너무 놀라웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그간 TDD없이 무작정 꼼꼼히 분석하고 개발하기 위해 노력한 과거의 내가 얼마나 비효율적이었는지 몸소 느낄수 있어 충격적이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TDD로 개발하니 상상이상으로 시간이 많이 걸려 전반적인 계획들이 딜레이되었지만, 그에 비해 내가 얻은것이 압도적으로 많았다. 지금 생각해보니 실제 전체적으로 보았을때 이런 TDD적인 방법이 정답이라 생각느꼈다. 그저 악으로 깡으로 꼼꼼히 개발하자라는 마음으로 개발하고 테스트를 한 뒤 이후의 에러와 버그를 꼼꼼히 잡는 것보다, 시스템적으로 자연스럽고 강제로 최대한 틈없이 단단하게 개발도록 하는것이 근본적으로 더 좋은 순서이고 맞는것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 4번의 TDD 미션을 거치며, 왜 TDD가 페어 프로그래밍과 궁합이 좋다고 하는지 조금은 알 것 같았다. 테스트를 설계하고, 작은 단위로 구현하고, 리팩토링하는 사이클 속에서 누군가와 함께 대화하며 고민했다면, 이 모든 장점이 훨씬 더 크게 증폭되었을 것 같다는 아쉬움이 자주 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한편으로로 장기 프로젝트에서 실제로 수정 사항이 계속 쌓이고, 이를 반영하며 유지보수하는 과정을 충분히 경험하지 못한 점도 아쉽다. 이번 오픈 미션을 통해 TDD의 &amp;ldquo;초기 개발 단계&amp;rdquo;는 온전히 체험했지만, TDD의 가장 큰 장점 중 하나인 &amp;ldquo;장기적인 유지보수와 변화 대응&amp;rdquo;을 몸으로 느껴보지 못했기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미션 마지막 시간을 보내며 이런 아쉬움이 남는 동시에, 본 교육 과정에서 페어 프로그래밍을 하며 TDD를 함께 적용하게 될 날을 상상하니 뭔가 가슴이 벅차며 더욱 나의 동기가 폭발하는것을 느꼈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;꼭 합격해서 크루원으로서 많은 활동을 하며 이런 가슴벅함을 실제로 느끼고 싶다 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&amp;ldquo;요구 사항 탐색 도구 로서의 TDD&amp;rdquo;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;편의점 미션에서 프로모션 계산 로직을 TDD로 만들면서, 처음에는 &amp;ldquo;2+1이면 3개 사면 1개 공짜&amp;rdquo; 정도만 떠올렸고, 단순히 그 케이스만 통과하면 충분하다고 생각했다. 그런데 테스트 코드를 직접 작성하다 보니 자연스럽게 더 많은 질문이 생겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;그럼 5개 사면 어떻게 되지?&amp;rdquo;, &amp;ldquo;6개에서는 몇 개가 무료로 나가야 하지?&amp;rdquo;, &amp;ldquo;7개면?&amp;rdquo;, 이런 애매한 케이스들이 테스트 메서드 이름을 짓는 순간부터 눈에 들어오기 시작했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트를 통해 5개, 6개, 7개 같은 케이스를 하나씩 적어보면서, 결국 &amp;ldquo;세트 단위(2+1이면 3개씩 끊어서)&amp;rdquo;로 계산해야 한다는 규칙을 스스로 발견했다. 만약 구현부터 시작했다면 &amp;ldquo;대충 나눠서&amp;rdquo; 때려 넣었을 가능성이 높았고, 나중에 엣지 케이스에서 이상한 값이 나와 디버깅에 시간을 썼을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 작년에는 이렇게 세부 사항에 대해 깊이 생각하지 않고 그저 그때 그때 로직에 맞게 동작하도록 구현했다.그리고 테스트를 돌린 제출 전날 부터 이런 부분 부분 구멍이 있던 로직들을 뜯어 고치느라 모든것이 꼬였었다. TDD는 &amp;ldquo;미리 구현한 로직에 테스트를 맞추는&amp;rdquo; 게 아니라, 테스트를 작성하면서 요구사항 자체를 다시 정의하게 만드는 도구라는 걸 체감했다. 프로모션 재고 부족 케이스와 멤버십 할인 로직에서도 반복됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;프로모션 재고가 7개인데, 손님이 10개를 담으면 몇 개가 프로모션 적용이지?&amp;rdquo;라는 질문을 테스트 코드로 정면 돌파하게 됐다. 처음에는 7개 전부를 프로모션으로 볼지, 아니면 세트가 완성되는 6개까지만 볼지 헷갈렸는데, 테스트를 작성하며 요구사항 문구를 다시 읽어보니 &amp;ldquo;프로모션 재고 내에서만 세트 단위로 혜택을 준다&amp;rdquo;는 규칙이 자연스럽게 도출됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 결과, &amp;ldquo;6개(두 세트)는 프로모션, 나머지 4개는 정가&amp;rdquo;라는 구체적인 시나리오를 테스트로 못 박을 수 있었고, 구현도 그 테스트에 맞춰 명확하게 정리할 수 있었다. 처음엔 그냥 &amp;ldquo;총 금액의 30%를 할인해주면 되겠지?&amp;rdquo;라고 막연히 생각했는데, 프로모션과 멤버십을 동시에 생각하다 보니 질문이 꼬였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;프로모션으로 이미 할인된 금액에도 또 멤버십 할인을 얹어야 하나?&amp;rdquo;, &amp;ldquo;프로모션 미적용 금액만 기준으로 잡는 게 맞나?&amp;rdquo; 이 애매한 부분을 테스트에 명시하려고 하니 자연스럽게 규칙이 정리됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 에너지바처럼 프로모션이 없는 상품만 멤버십 할인 대상이 되고, 2+1으로 이미 혜택을 본 콜라는 제외하는 구조를 테스트로 먼저 못 박았다. 이 과정이 없었다면, 구현마다 기준이 조금씩 달라지고, 테스트도 케이스마다 설명할 수 없는 미묘한 차이가 생겼을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경험 전체를 통해 내가 머릿속으로 &amp;ldquo;이렇게 동작하겠지&amp;rdquo;라고 가정하던 부분들을, TDD는 &amp;ldquo;테스트로 먼저 적나라하게 드러내 보라&amp;rdquo;고 강제로 요구한다는 점이다 ! 테스트를 작성하는 순간, 모호한 요구사항이 구체적인 숫자, 케이스, 조건으로 내려오고, 그 과정에서 숨은 규칙들을 미리 발견할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 TDD는 버그를 잡는 도구를 넘어서, 요구사항을 다시 읽게 만들고, 애매한 부분을 질문하게 만들고, 설계를 더 명확하게 만드는 &amp;ldquo;탐색 도구&amp;rdquo; 역할을 아주 제대로 해줬다 ! &amp;ldquo;이렇게 돌아가겠지&amp;rdquo;가 아니라, &amp;ldquo;그렇게 돌아가야 한다면, 테스트로 먼저 써보자&amp;rdquo;라는 태도를 몸으로 익힌 순간이었다.이 하나의 태도와 접근이 너무 큰 영향을 미친다는걸 작년의 경험들과 비교해보며 아주 뼈저리게 느꼈다 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&amp;ldquo;설계 피드백 도구로서의 TDD&amp;rdquo;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Product와 Promotion의 관계를 설계하면서, 처음에는 단순히 &amp;ldquo;상품은 프로모션을 가진다&amp;rdquo;라고 가볍게 생각했다. 하지만 테스트를 먼저 작성해보니, 자연스럽게 &amp;ldquo;모든 상품이 프로모션을 가지는 건 아니지 않은가?&amp;rdquo;, &amp;ldquo;그럼 프로모션이 없는 상품은 어떻게 표현해야 하지?&amp;rdquo;, &amp;ldquo;상품이 프로모션을 아는 게 맞을까?&amp;rdquo;를 고민하게 되었다. 그래서 테스트에서 product.hasPromotion() 같은 메시지를 먼저 써보았고, 그 문장을 읽는 순간 &amp;ldquo;상품이 자기 프로모션 여부를 아는 구조가 자연스럽다&amp;rdquo;는 결론에 도달했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 결과, Product가 Promotion을 Optional하게 소유하는 구조로 설계했고, 프로모션이 없는 상품도 깔끔하게 표현할 수 있었다. 이건 단순한 구조 선택이 아니라, 테스트가 요구하는 문장을 통해 도메인 관계를 다시 정리한 결과였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로 고민이 된 건 &amp;ldquo;프로모션 계산은 누가 해야 하는가?&amp;rdquo;였다. 처음에는 Product가 계산하면 될 것 같았고, 그다음에는 Promotion이 하는 게 맞지 않나 싶었다. 하지만 테스트를 작성하다 보니, 이 계산은 단순히 &amp;ldquo;공짜 개수 계산&amp;rdquo;만이 아니라, 재고, 날짜, 추가 구매 제안, 정가 결제 여부까지 포함된 복합적인 행위였다 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 Product에게 넘기면 Product가 지나치게 많은 걸 알게 되고, Promotion에게 맡기면 프로모션이 상품 재고와 구매 흐름까지 침범하게 되는 문제가 있었다. 테스트에서 calculator.calculate(product, quantity, date)라는 형태의 메시지를 쓰게 되면서,자연스럽게 PurchaseCalculator라는 별도의 객체가 필요하다는 결론으로 이어졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역시나 처음부터 계획한 게 아니라, 테스트 코드가 요구하는 메시지를 따라가다 보니 나온 결과였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 계산 결과를 어떤 형태로 반환할지 고민하는 과정에서, payQuantity, freeQuantity만으로는 부족하다는 사실을 테스트 작성 중에 깨달았다. 추가 구매 제안 여부, 정가 결제 수량, 상태 메시지까지 필요해지면서, &amp;ldquo;이건 단순한 결과가 아니라 하나의 의미 있는 결과 객체다&amp;rdquo;라는 감각이 생겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 PurchaseResult라는 객체를 만들게 되었고, 필드가 점점 늘어나는 걸 보며 생성자의 파라미터가 감당하기 어려워지는 시점에 Builder 패턴을 도입해야겠다는 판단을 내리게 됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것도 설계 이론에서 출발한 게 아니라, 테스트를 쓰면서 &amp;ldquo;이 객체를 생성하기 너무 어렵다&amp;rdquo;는 불편함을 몸으로 느낀 결과였다. 설계가 어색할 때, 테스트가 그 불편함을 드러내준다는게 포인트였다 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메시지를 쓰기 어려우면설계가 잘못됐다는 신호였고, 객체 생성이 복잡해지면, 역할이 제대로 정리되지 않았다는 신호였으며, 파라미터가 기하급수적으로 늘어나면, 책임이 과도하게 한 곳에 몰렸다는 신호였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 그 신호들을 테스트 작성 과정에서 계속 마주했기 때문에, 나는 하나의 큰 설계를 한 번에 만드는 게 아니라, 테스트를 통해 문제를 인식하고, 그때그때 설계를 조정하는 소중한 경험을 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&amp;ldquo;리팩토링 안전망으로서의 TDD&amp;rdquo;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미션 후반부에 크고 작은 리팩토링을 하는 과정, 특히 프로그래밍 요구사항을 반영하는 시간에 TDD의 힘을 또한번 제대로 느꼈다. 재고 차감 로직 처음 구현은 &amp;ldquo;프로모션 재고가 모자라면, 그냥 일반 재고에서 빼자&amp;rdquo; 정도의 단순한 if-else 로직이었는데, 테스트를 돌려보니 &amp;ldquo;프로모션 재고가 0이 되어야 하는 상황에서 3개가 그대로 남아 있는&amp;rdquo; 버그가 사전에 바로 드러났다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;머리로만 읽을 땐 당연해 보이던 코드였는데, 테스트를 통과시키려면 &amp;ldquo;프로모션 재고를 먼저 다 쓰고, 남은 수량을 일반 재고에서 빼야 한다&amp;rdquo;는 진짜 규칙대로 고쳐야 했다. 이때 느낀 건, &amp;ldquo;읽을 땐 맞아 보이는 코드도 테스트 앞에서는 솔직해진다&amp;rdquo;는 거였다. 가장 큰 산은 PurchaseCalculator의 대대적인 리팩토링이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 하나의 메서드 안에 프로모션 여부 확인, 추가 구매 제안, 재고 부족 시 정가 결제, 정상 프로모션 적용이 줄줄이 이어진 100줄은 아득히 넘는 코드였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종종 읽을 때마다 여기선 뭐 하지 이 조건은 저기랑 겹치지 않나 라는 느낌이 들었지만, 이미 잘 돌아가고 있기도 했고, 건들면 여러 곳에서 문제가 생기지 않을까 하는 두려움도 꽤나 들었다. 하지만 TDD 과정의 살아있는 테스트 코드를 믿으며 테스트와 리팩토링을 반복하며 하나씩 잘 리팩토링을 마칠 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;긴 메서드를 쪼깨고, 클래스를 새로 떠올리고, 이름을 바꾸고 책임을 옮길 때 &amp;ldquo;이렇게 바꿔도 될랑가&amp;rdquo;하는 질문에 답해준건 나의 감각과 근거없는 용기가 아니라 항상 똑같은 대답을 해주는 테스트 결과 색들이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TDD 사이클이 주는 심리적 장점들이 역시나 너무 든든했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&amp;ldquo;View에서 Domain 참조 - DTO 도입 vs 직접 참조&amp;rdquo;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미션을 진행하며 늘 &amp;ldquo;View가 Domain을 어디까지 알아도 되는가?&amp;rdquo;,&amp;ldquo;이 상황에서 DTO를 도입하는 게 맞는가?&amp;rdquo;에 대해 고민했다. 직관적으로 이해할때는 전혀 문제가 없었지만 roduct.getPromotion().getName() 와 같은 체이닝들이 View에서 자주 보이며 계층 분리관점에 대해 자꾸만 의문이 남았고 DTO를 떠올렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DTO를 도입하면 분명 장점은 있다. view는 DTO같은 단순한 구조만 알고, Domain이 바뀌더라도 &amp;ldquo;DTO 변환 로직&amp;rdquo;만 고치면 되기 때문이다. 하지만 3주차에 적용해본 경험과 함께 현재 상황에 대해 다시 고민해 보니 다른 결론에 도달했다. 어디까지나 단일 프로세스의 콘솔 프로그램이고, API도 없고, JSON 직렬화도 없고, 외부 시스템과의 통신도 없다. View가 하는 일은 &amp;ldquo;그냥 예쁘게 출력해주는 것&amp;rdquo;이 전부다 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상황에서 DTO를 도입하면, DTO 클래스가 여러 개 생기고, DTO 변환 코드가 여기저기 생기고, 테스트 코드도 DTO를 추가로 다뤄야 하고, 정작 &amp;ldquo;도메인 변경 파급&amp;rdquo;은 지금 구조에서도 거의 크지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 현재 DTO는 실익보다 복잡도와 관리 비용이 훨씬 더 크기 때문에 최종적으로는 &amp;ldquo;DTO는 도입하지 않고, 대신 Domain을 안전하게 노출하는 방향&amp;rdquo;으로 결정했다. 조회 전용 getter는 허용하되, 컬렉션은 방어적 복사나 불변 리스트로 반환하고, 비즈니스 로직은 View로 끌어가지 않고 Domain 안에 두는 방식으로 정리했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &amp;ldquo;View가 Domain을 읽는 것은 허용하지만, Domain의 로직을 빼내어 View에서 조합하지는 않는다&amp;rdquo;라는 기준을 세웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;Getter를 지양하라&amp;rdquo;는 말은 &amp;ldquo;getter로 다 꺼내서 밖에서 처리하지 마라&amp;rdquo;에 더 가깝지, &amp;ldquo;조회용 Getter 자체를 죄악시하라&amp;rdquo;는 뜻은 아니라는 것 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작은 콘솔 프로그램에서까지 DTO, 계층, 패턴을 전부 끌어오는 건 OOP 학습을 한다는 핑계로 스스로를 복잡도 지옥에 밀어 넣는 일일 수도 있다는 것 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 설계는 &amp;ldquo;이 프로젝트의 크기와 목적에 비해 가치가 있냐?&amp;rdquo;로 판단해야 한다는 것 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;덕분에 그간 프리코스를 진행하며 고민한 여러 트레이드오프들과 YAGNI는 내가 합리화를 하고 있는게 아닐까 라는 추상적인 의문에 명확한 답을 내릴수 있어 너무 뿌듯했다!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&amp;ldquo;더 나은 설계의 환상 - YAGNI와 TDD의 본질&amp;rdquo;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능 구현을 마치고 리팩토링 사이클을 진행 하던 도중 나도 모르게 머릿속이 또 한 번 &amp;ldquo;더 나은 설계&amp;rdquo; 욕심으로 가득기 시작했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ConvenienceStore는 상품 저장소 역할을 하는 products 맵을 들고 있으면서, 구매 검증도 하고, PurchaseCalculator를 통해 계산도 위임하고, PurchaseContext도 생성&amp;middot;관리하는 등 겉으로 보기엔 책임이 굉장히 많아 보였다. &amp;ldquo;이건 도메인 객체인가? 애플리케이션 서비스인가? 아니면 그냥 저장소인가?&amp;rdquo; &amp;ldquo;책임이 너무 많은 거 아닌가? Repository랑 Service로 쪼개야 하는 거 아닌가?&amp;rdquo;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2주차에 컨트롤러 테스트를 위해 구조를 과하게 분리했던 경험과 3주차에 Service 계층을 도입했다가 다시 철회했던 경험이 있었지만 나는 또다시 &amp;ldquo;더 객체지향적으로 보이는 구조&amp;rdquo;를 만들고 싶은 마음에 끌리고 있었다. products를 DB로 옮길 수도 있으니 Repository 인터페이스를 도입할까? 컨트롤러를 더 가볍게 만들기 위해 PurchaseService를 빼야 하나? PurchaseContext는 엔티티 같은데, 이렇게 컨트롤러에서 막 수정해도 되나? 와 같은 생각이 들었다. 또한 Repository를 도입하면 책임 분리가 명확해지고, Service가 있으면 계층 구조가 더 예뻐지고, Context를 더 철저하게 감추면 &amp;ldquo;더 캡슐화된 느낌&amp;rdquo;이 난다고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 고민을 할수록 점차 다시 머리가 복잡해져 상황을 정리하는 과정에서 이전 경험들이 다시금 떠올랐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨트롤러 테스트를 위해 수많은 Mock과 인터페이스를 도입했지만, 실제로 얻는 가치에 비해 유지 비용이 훨씬 더 컸던 경험과 서비스를 도입했지만 결국 그저 Mapper 였던 경험과 같이 TDD와 OOP를 동시에 만족시키려다, 지금 필요한 것보다 &amp;ldquo;이상적인 설계&amp;rdquo;에 더 집착해버렸던 순간들을 ! 그 후 다시 나 스스로에게 되물어봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 코드에 실제 버그가 있나? 없다. 모든 테스트가 통과했고, 시나리오도 정상 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 코드가 읽기 어렵나? 아니다. 오히려 구조가 단순하고, 읽으면 바로 이해된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변경하기 어려운 구조인가? 아니다. 도메인과 계산 로직은 이미 테스트로 잘 감싸져 있고, 리팩토링하기에도 부담이 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 프로젝트에서 DB, 외부 API, 복잡한 인프라를 쓸 예정인가? &amp;rarr; 아니다. 학습용 콘솔 프로그램이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데도 나는 ConvenienceStore를 억지로 Repository/Service 구조로 쪼개려 하고 있었다 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 문제를 해결하려는 게 아니라, &amp;ldquo;더 객체지향적으로 보이는 구조&amp;rdquo;를 만들고 싶을 뿐이었다 !!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;전문적인 설계처럼 보이고 싶다&amp;rdquo;, &amp;ldquo;배운 패턴들을 실제 코드에 써먹고 싶다&amp;rdquo;, &amp;ldquo;지금은 작지만, 나중에 확장될 수 있으니까 미리 준비해두자&amp;rdquo;와 같은 욕심들이 모여서, 실제로는 필요하지도 않은 추상화와 계층을 계속 상상하고 있었다는 걸 인정하게 됐다. &amp;ldquo;더 나은 설계&amp;rdquo;를 쫓다가 &amp;ldquo;이미 충분히 좋은 설계&amp;rdquo;를 놓칠 뻔했다. 충분하다는 가치를 내가 너무 무시하고 있었다 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2주차 부터 비슷한 고민을 했지만 전혀 자괴감이 들지 않았다. 왜냐하면 1,2,3,4주차 미션을 진행하며 이러한 경험들이 쌓이며 분명 개선되는게 느껴졌기 때문이다 ! 무엇보다 똑같은 문제에 대해 똑같이 고민하는 것이 아니라, 여러 상황에 대한 경험치를 쌓는 과정으로 느껴져서 그저 뿌듯할 뿐이었다 ! 이전 학교 수업에서 &amp;ldquo;결국 이런 저런 이론보다 실무에서는 전문가들의 경험을 더 중요시 여긴다&amp;rdquo;는 당시에는 전혀 이해하지 못하고 와닿지 않던 말이 이제는 이해가 된다는 점 또한 너무 신기했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;202&quot; data-start=&quot;196&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;마치며&lt;/b&gt;&lt;/h2&gt;
&lt;p data-end=&quot;293&quot; data-start=&quot;204&quot; data-ke-size=&quot;size16&quot;&gt;이번 4차 회고는 결과를 정리하는 글이 아니라, &lt;b&gt;내가 왜 이 과정을 시작했고, 무엇을 깨뜨렸고, 무엇을 남겼는지 돌아보는 정리의 순간&lt;/b&gt;이라고 느껴진다. 처음에는 &amp;ldquo;테스트를 좀 더 잘 써보고 싶다&amp;rdquo;는 단순한 욕심에서 시작했지만, 이제는 그보다 훨씬 본질적인 질문에 닿아 있다고 느낀다. &lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;나는 지금까지 &lt;/span&gt;테스트를 도구로 썼던 걸까, 아니면 테스트에 끌려 다녔던 걸까?&lt;/p&gt;
&lt;p data-end=&quot;478&quot; data-start=&quot;452&quot; data-ke-size=&quot;size16&quot;&gt;선정 배경에서 스스로에게 던졌던 질문들이 있다.&lt;/p&gt;
&lt;p data-end=&quot;555&quot; data-start=&quot;480&quot; data-ke-size=&quot;size16&quot;&gt;왜 테스트를 했는데도 버그를 늦게 발견했을까?&lt;br /&gt;왜 테스트가 있었는데도 리팩토링이 불안했을까?&lt;br /&gt;왜 테스트가 설계를 돕지 못했을까?&lt;/p&gt;
&lt;p data-end=&quot;653&quot; data-start=&quot;557&quot; data-ke-size=&quot;size16&quot;&gt;이 질문들의 공통된 원인은 분명했다.&lt;br /&gt;나는 테스트를 &amp;ldquo;정답을 검증하는 수단&amp;rdquo;으로만 사용했고, &amp;ldquo;요구사항을 발견하고 설계를 이끄는 도구&amp;rdquo;로는 쓰지 못하고 있었다는 것.&lt;/p&gt;
&lt;p data-end=&quot;712&quot; data-start=&quot;655&quot; data-ke-size=&quot;size16&quot;&gt;이번 1~4차 회고를 거치며 그 오해는 조금씩 무너졌고, 테스트에 대한 관점은 서서히 바뀌었다.&lt;/p&gt;
&lt;p data-end=&quot;720&quot; data-start=&quot;714&quot; data-ke-size=&quot;size16&quot;&gt;테스트는 &lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;버그를 잡는 마지막 방어선이 아니라, &lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;버그를 만들지 않기 위해 끊임없이 질문하는 도구였고,&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;구현을 뒷받침하는 부속물이 아니라, &lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;설계를 이끄는 출발점이 될 수 있다는 것을 &lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;이제는 실제 경험을 통해 받아들이게 되었다 !!&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;895&quot; data-start=&quot;873&quot; data-ke-size=&quot;size16&quot;&gt;처음 세웠던 네 가지 목표를 다시 생각해보면 완벽하게 도달했다고 말할 수는 없겠지만, 적어도 이제는 그 목표들이 막연한 이상이 아니라 실제 코드와 경험 위에서 체득되고 있다는 감각이 있다.&lt;/p&gt;
&lt;p data-end=&quot;1163&quot; data-start=&quot;1108&quot; data-ke-size=&quot;size16&quot;&gt;이전의 나는 &amp;ldquo;설계를 먼저 고민하고, 그에 맞게 구현하고, 나중에 테스트를 덧붙이는 구조&amp;rdquo;였다면 지금의 나는 &amp;ldquo;테스트를 통해 요구사항을 구체화하고, 그 위에서 설계 방향을 정하고, 그 설계를 다시 테스트로 검증하는 흐름&amp;rdquo;을 경험하고 있다.&lt;/p&gt;
&lt;p data-end=&quot;1304&quot; data-start=&quot;1252&quot; data-ke-size=&quot;size16&quot;&gt;이 차이는 생각보다 훨씬 크고, 이 차이가 바로 이번 TDD 여정의 핵심 성과라고 느낀다 !!&lt;/p&gt;
&lt;p data-end=&quot;1304&quot; data-start=&quot;1252&quot; data-ke-size=&quot;size16&quot;&gt;기술을 익혔다기보다 완전히 정신개조가 되었다는 느낌이 든다 !&lt;/p&gt;
&lt;p data-end=&quot;1372&quot; data-start=&quot;1335&quot; data-ke-size=&quot;size16&quot;&gt;이번 4차 회고까지 오면서 가장 크게 무너진 생각은 &quot;처음부터 끝까지 늘 잘 설계해야한다&quot;이다.&lt;/p&gt;
&lt;p data-end=&quot;1557&quot; data-start=&quot;1456&quot; data-ke-size=&quot;size16&quot;&gt;중요한 건 멋있는 구조를 한 번에 만드는 게 아니라, &lt;b&gt;틀려도 무너지지 않고, 바꿔도 무너지지 않는 상태를 유지하는 것&lt;/b&gt;이라는 걸&lt;br /&gt;TDD를 통해 직접 체감하게 되었다.&lt;/p&gt;
&lt;p data-end=&quot;1627&quot; data-start=&quot;1559&quot; data-ke-size=&quot;size16&quot;&gt;그 상태를 가능하게 해준 건 설계 감각이 아니라, 리팩토링을 두려워하지 않게 해 준 TDD 사이클 속 살아있는&amp;nbsp;&amp;nbsp;&lt;b&gt;테스트라는 안전망&lt;/b&gt;이었다 !&lt;/p&gt;
&lt;p data-end=&quot;1711&quot; data-start=&quot;1647&quot; data-ke-size=&quot;size16&quot;&gt;이 여정은 TDD를 마스터하기 위한 여정이 아니라 &lt;b&gt;내가 가졌던 오해를 하나씩 깨는 여정&lt;/b&gt;이었다.&lt;/p&gt;
&lt;p data-end=&quot;1711&quot; data-start=&quot;1647&quot; data-ke-size=&quot;size16&quot;&gt;테스트를 나중에 붙여도 된다는 오해, &lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;설계를 먼저 완벽히 해야 한다는 오해, &lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;계층은 많을수록 좋다는 오해, &lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;테스트가 많으면 무조건 좋은 거라는 오해 !&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;1867&quot; data-start=&quot;1810&quot; data-ke-size=&quot;size16&quot;&gt;이번 경험을 통해 그 오해들이 조금은 흐릿해졌고, 대신 나만의 기준이 조금씩 생겨나기 시작했다.&lt;/p&gt;
&lt;p data-end=&quot;1918&quot; data-start=&quot;1869&quot; data-ke-size=&quot;size16&quot;&gt;아직 부족하고, 분명 또 흔들릴 것이고, 실무에서는 다시 다른 고민이 시작되겠지만 지금 나는 적어도 &lt;b&gt;테스트를 통해 사고하는 개발자&lt;/b&gt;로 한 걸음 들어왔다는 확신은 있다. 이러한 정신개조 그 자체로 너무나 큰 수확이다 !&lt;/p&gt;
&lt;p data-end=&quot;1918&quot; data-start=&quot;1869&quot; data-ke-size=&quot;size16&quot;&gt;너무 많은 걸 느끼고 깨달은 이번 오픈미션 경험이 너무나 소중하다 ! 이렇나 경험들과 감정들이 더욱 더 크루원으로서 우테코에 합류하고자 하는 나의 열정을 불태운다 ! 내년 2월 페어프로그래밍이 너무 너무 기대된다 딱대 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>외부활동/우아한테크코스 [프리코스]</category>
      <author>softmoca__</author>
      <guid isPermaLink="true">https://softmoca.tistory.com/564</guid>
      <comments>https://softmoca.tistory.com/564#entry564comment</comments>
      <pubDate>Sat, 22 Nov 2025 20:10:57 +0900</pubDate>
    </item>
    <item>
      <title>[우아한테크코스 8기] 프리코스 오픈미션 3차 회고- 로또 TDD</title>
      <link>https://softmoca.tistory.com/560</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/coQeMr/dJMcahXauhJ/rDflfc9FaT20hNmKuzYVa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/coQeMr/dJMcahXauhJ/rDflfc9FaT20hNmKuzYVa1/img.png&quot; data-origin-width=&quot;825&quot; data-origin-height=&quot;536&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.0921%; margin-right: 10px;&quot; data-widthpercent=&quot;32.86&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/coQeMr/dJMcahXauhJ/rDflfc9FaT20hNmKuzYVa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcoQeMr%2FdJMcahXauhJ%2FrDflfc9FaT20hNmKuzYVa1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;825&quot; height=&quot;536&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTvvn2/dJMcac2zFjw/N1bCbwrklKO5tJLxq4iP7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTvvn2/dJMcac2zFjw/N1bCbwrklKO5tJLxq4iP7K/img.png&quot; data-origin-width=&quot;782&quot; data-origin-height=&quot;472&quot; data-is-animation=&quot;false&quot; style=&quot;width: 34.5441%; margin-right: 10px;&quot; data-widthpercent=&quot;35.37&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTvvn2/dJMcac2zFjw/N1bCbwrklKO5tJLxq4iP7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTvvn2%2FdJMcac2zFjw%2FN1bCbwrklKO5tJLxq4iP7K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;782&quot; height=&quot;472&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FPoFe/dJMcadUH1cW/2R07r4mW47cs1Fk3My0BHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FPoFe/dJMcadUH1cW/2R07r4mW47cs1Fk3My0BHK/img.png&quot; data-origin-width=&quot;655&quot; data-origin-height=&quot;440&quot; data-is-animation=&quot;false&quot; style=&quot;width: 31.0383%;&quot; data-widthpercent=&quot;31.77&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FPoFe/dJMcadUH1cW/2R07r4mW47cs1Fk3My0BHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFPoFe%2FdJMcadUH1cW%2F2R07r4mW47cs1Fk3My0BHK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;655&quot; height=&quot;440&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6W1Rd/dJMcaiod6jT/kSMebCRIYracPERgS1xq1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6W1Rd/dJMcaiod6jT/kSMebCRIYracPERgS1xq1k/img.png&quot; data-origin-width=&quot;805&quot; data-origin-height=&quot;512&quot; data-is-animation=&quot;false&quot; style=&quot;width: 35.5387%; margin-right: 10px;&quot; data-widthpercent=&quot;36.38&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6W1Rd/dJMcaiod6jT/kSMebCRIYracPERgS1xq1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6W1Rd%2FdJMcaiod6jT%2FkSMebCRIYracPERgS1xq1k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;805&quot; height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/npJ1A/dJMcacatCu5/wW78rLXUxmcOLwYvBEUdsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/npJ1A/dJMcacatCu5/wW78rLXUxmcOLwYvBEUdsK/img.png&quot; data-origin-width=&quot;808&quot; data-origin-height=&quot;588&quot; data-is-animation=&quot;false&quot; style=&quot;width: 31.0606%; margin-right: 10px;&quot; data-widthpercent=&quot;31.8&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/npJ1A/dJMcacatCu5/wW78rLXUxmcOLwYvBEUdsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnpJ1A%2FdJMcacatCu5%2FwW78rLXUxmcOLwYvBEUdsK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;808&quot; height=&quot;588&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQvXll/dJMcaiod6jZ/xdzIcw3wZBppNUCeWvcSL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQvXll/dJMcaiod6jZ/xdzIcw3wZBppNUCeWvcSL1/img.png&quot; data-origin-width=&quot;840&quot; data-origin-height=&quot;611&quot; data-is-animation=&quot;false&quot; style=&quot;width: 31.0752%;&quot; data-widthpercent=&quot;31.82&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQvXll/dJMcaiod6jZ/xdzIcw3wZBppNUCeWvcSL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQvXll%2FdJMcaiod6jZ%2FxdzIcw3wZBppNUCeWvcSL1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;840&quot; height=&quot;611&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/softmoca/java-lotto-8/tree/softmoca-tdd&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/softmoca/java-lotto-8/tree/softmoca-tdd&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1764213344580&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - softmoca/java-lotto-8&quot; data-og-description=&quot;Contribute to softmoca/java-lotto-8 development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/softmoca/java-lotto-8/tree/softmoca-tdd&quot; data-og-url=&quot;https://github.com/softmoca/java-lotto-8&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/fDVG1/hyZNLP0GH9/OV8a8lkKZQbP3ghcWOb7R1/img.png?width=1200&amp;amp;height=600&amp;amp;face=988_120_1036_172,https://scrap.kakaocdn.net/dn/hHfaG/hyZOkqvlBe/F3sri06SEtRkXROeA88nS0/img.png?width=1200&amp;amp;height=600&amp;amp;face=988_120_1036_172&quot;&gt;&lt;a href=&quot;https://github.com/softmoca/java-lotto-8/tree/softmoca-tdd&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/softmoca/java-lotto-8/tree/softmoca-tdd&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/fDVG1/hyZNLP0GH9/OV8a8lkKZQbP3ghcWOb7R1/img.png?width=1200&amp;amp;height=600&amp;amp;face=988_120_1036_172,https://scrap.kakaocdn.net/dn/hHfaG/hyZOkqvlBe/F3sri06SEtRkXROeA88nS0/img.png?width=1200&amp;amp;height=600&amp;amp;face=988_120_1036_172');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - softmoca/java-lotto-8&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to softmoca/java-lotto-8 development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;시작하며&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드디어 처음 나에게 TDD를 경험해 보고 싶게 만든 3주차 로또 미션을 마쳤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;벌써 3번째 사용해 보는데 사용할수록 너무 TDD의 장점을 크게 느낀다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무엇보다 요구사항 탐색과 리팩토링 안정망 도구로서의 TDD의 힘이 가장 잘 체감 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그에비해 설계 피드백 도구로서의 TDD의 장점은 온전히 느끼고 있지 못한것 같다. 2주차 회고에서도 느꼈지만 이러한 OOP적인 설계를 녹여내기 위해서는 책한권, 이론 한줄 안은것이 아닌 무엇보다 경험이 가장 중요한것 같다. 아주 작고 사소하지만, 혹은 잘못되었을지 모르지만 그래도 직접 부딪혀보며 확실히 이런 경험치가 싸여 성장하고 있음이 느껴져서 너무 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&amp;ldquo;요구사항 탐색 &amp;amp; 설계 피드백 도구로서의 TD&amp;rdquo;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rank를 대할 때 의식적으로 더 신경을 쓴 만큼 더욱 직접으로 TDD의 위력을 체감할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 &amp;ldquo;일치 개수 + 보너스 여부로 등수를 구한다&amp;rdquo;는 단순한 이해로 출발했지만, 테스트를 작성하면서 요구사항이 훨씬 구체적으로 드러났다. 특히 Rank.of(matchCount, hasBonus) 형태의 테스트를 작성하면서 등수는 단순한 조건 분기가 아니라, 각 등수가 스스로 자신의 조건을 판단해야 한다는 설계 방향으로 자연스럽게 흘러갔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정에서 Rank를 enum으로 정의하고, 각 등수에 일치 개수, 보너스 필요 여부, 상금을 가지게 하며 책임을 응집시키는 구조로 개선할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 &amp;ldquo;낙첨은 어떻게 표현하지?&amp;rdquo;라는 질문에서 null이나 플래그가 아닌 Optional&amp;lt;Rank&amp;gt;를 사용하기로 결정했는데, 이는 단순한 구현 선택이 아니라 테스트 작성 과정에서 &amp;lsquo;결과가 없을 수 있음&amp;rsquo;을 명확히 인식했기 때문이었다. 보너스 번호 로직에서 처음에는 &amp;ldquo;보너스가 없으면 탈락&amp;rdquo;이라는 잘못된 가정을 하고 구현하려 했지만, 테스트를 통해 &amp;ldquo;1등, 3등, 4등, 5등은 보너스와 무관하게 결정된다&amp;rdquo; 라는 실제 요구사항을 다시 정확히 이해하게 되었고,이 덕분에 3주차 로또 미션에서 겪었던 동일한 유형의 버그를 사전에 차단할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 다른 고민은 로또 번호의 정렬 책임을 어디에 둘지였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력 시 정렬할지, 아니면 Lotto 생성 시 정렬할지를 고민했는데, 테스트를 작성하며 &amp;ldquo;Lotto는 어디에서든 사용될 수 있는 도메인 객체&amp;rdquo;라는 점을 다시 생각하게 되었고, 결국 생성 시점에 정렬하여 불변 조건을 보장하는 방향으로 설계를 선택했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경험을 통해 단순히 기능을 구현하는 것이 아니라, 테스트를 통해 요구사항을 더 정확히 이해하고, 그 이해를 바탕으로 설계를 결정하는 경험을 하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TDD는 단순한 개발 기법이 아니라, 내가 무엇을 만들고 있는지를 끊임없이 되묻는 사고 도구라는 사실을 체감한 순간이었다. 과거에 1차원적으로 출력의 책임으로만 보았던 부분을 TDD를 통해 더 깊게 고민하며 지난 결정과 다른 결론이 나온점 또한 인상 깊었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무엇보다 당장의 시야에서 보이는 근거가 확실했으며 TDD라는 점진적으로 개선할수 있는 시스템과 함께여서 이전 트레이드오프에 대해 여러 고민하는 과정으로 혼란스러움이 없다는 점이 너무 크게 체감 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&amp;ldquo;설계 피드백 도구 &amp;amp; 리팩토링 안전망으로서의 TDD&amp;rdquo;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3주차 프리코스에서는 Lotto의 필드가 수정할수 없어서 아쉬웠다. 이 부분을 만질수 있다면 유기적으로 많은 이점을 가질수 있을것 같다는 생각했기 떄문이다. 그리고 TDD를 진행하며 이런 부분을 개발 초기부터 온전히 고려하게 되고 과거의 나의 생각이 맞았음을 느끼게되어 뿌듯했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음Lotto를 개발할 때, 숫자 범위 검증을 추가하기 위해 먼저 테스트를 작성했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 Lotto 내부에서 직접 1~45 검증 로직을 구현했지만,리팩토링 단계에서 이미 LottoNumber에 동일한 검증 로직이 존재한다는 사실을 발견했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 고민은 &amp;ldquo;이미 있는 책임을 다시 가져올 것인가, 아니면 재사용할 것인가&amp;rdquo;였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 중복된 검증 로직이 두 곳에 분산되는 것보다, Lotto가 List&amp;lt;Integer&amp;gt;가 아닌 List&amp;lt;LottoNumber&amp;gt;를 보유하도록 설계를 변경하는 쪽이 더 옳다고 판단했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 변경하면 로또 번호의 모든 검증 책임이 LottoNumber에 모이게 되고, Lotto는 조합 역할에만 집중할 수 있었다.이 과정에서 가장 인상 깊었던 점은, 테스트가 이미 존재했기 때문에 기존 동작을 두려움 없이 유지한 채 리팩토링을 시도할 수 있었다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TDD가 없었다면, 중복을 발견하더라도 &amp;ldquo;괜히 건드렸다가 깨질까 봐&amp;rdquo; 그대로 두었을 가능성이 높았을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 WinningLotto를 설계하는 과정에서도 TDD가 큰 역할을 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 당첨 번호와 로또를 비교해 등수를 반환하는 기능을 어떻게 설계할지 막막했지만,테스트를 먼저 작성하면서 자연스럽게 match(Lotto)라는 메시지가 드러났고, 우승 번호 객체가 로또를 검사하고 Rank를 반환하는 구조로 정리할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 중요한 고민은 &amp;ldquo;WinningLotto가 직접 모든 계산을 할 것인가, 기존 객체들에게 역할을 나눠줄 것인가&amp;rdquo;였다. 결론적으로 WinningLotto는 조정자 역할만 수행하고,번호 비교는 Lotto, 등수 판단은 Rank에게 위임하는 구조가 더 객체지향적이라고 판단했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경험을 통해 객체를 잘 만들어두면, 나중에는 새로운 기능을 추가할 때 &amp;ldquo;조합만&amp;rdquo; 하면 된다는 말을 실제로 체감할 수 있었다. 그리고 그 과정 전체를 가능하게 해 준 것은 리팩토링에도 흔들리지 않는 테스트라는 안전망이었다. 무엇보다 2주차 OOP와 TDD 사이의 균형을 잡지못한 점이 큰 실책이있지만 이번 경험은 이전 경험들이 좋은 성장 발판으로 되돌아와 좋은 성장을 할수 있었다 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;Service 계층 도입과 철회 - 과도한 설계의 발견&amp;rdquo;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DTO를 도입한 이후에도 컨트롤러가 여전히 무겁다는 느낌이 강하게 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 LottoStatistics를 LottoResult로 변환하는 책임이 컨트롤러에 있는 것이 맞는지 계속 의문이 들었고, &amp;ldquo;Domain &amp;rarr; DTO 변환은 어디서 책임져야 할까?&amp;rdquo;라는 고민에서 Service 계층을 도입하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LottoResultService를 만들면서 도메인과 DTO 간 역할 분리가 깔끔해지고, 구조도 더 계층적으로 보이게 되었지만 이후 다시 코드를 보며 스스로에게 질문하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&amp;ldquo;지금 이 프로젝트에서 이게 정말 Service 계층이 맞는가?&amp;rdquo;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 프로젝트는 DB도 없고, 외부 시스템도 없고, 단순 콘솔 프로그램이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Service가 실제로 하는 일은 비즈니스 조율이 아니라 DTO 변환과 수익률 계산 정도에 불과했다. 이건 &amp;lsquo;비즈니스 흐름 관리자&amp;rsquo;가 아니라 사실상 Mapper에 가까운 역할이라는 걸 인식하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 나는 미래를 대비하겠다는 명분으로 현재 필요 없는 구조를 미리 만들어버린 것이라는 결론에 도달했고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Service 계층을 제거하고 해당 책임을 다시 구조적으로 자연스러운 위치로 되돌리는 결정을 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경험을 통해 &amp;ldquo;계층 분리는 많이 할수록 좋다&amp;rdquo;가 아니라 &amp;ldquo;프로젝트 규모와 복잡도에 맞는 수준의 분리만이 좋은 설계다&amp;rdquo;라는 걸 체감했고,TDD의 핵심 원칙인 YAGNI(지금 필요 없으면 만들지 않는다)를 이론이 아니라 실제 코드로 경험할 수 있었다. Service를 도입했다가 철회한 과정 자체가 내가 과도한 설계의 유혹을 직접 경험하고, 그 안에서 균형점을 찾아갔던 중요한 학습 포인트였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 3주차와 달리 서비스 계층에 대해 고민하는 과정과 도입하고 제거하는 과정에서 TDD 사이클 덕분에 훨씬 수월하게 고민하고 리팩토링을 할수 있다는 점을 느껴 너무 좋았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;6&quot; data-start=&quot;0&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;마치며&lt;/b&gt;&lt;/h2&gt;
&lt;p data-end=&quot;218&quot; data-start=&quot;8&quot; data-ke-size=&quot;size16&quot;&gt;처음 나를 TDD로 끌어당겼던 바로 그 3주차 로또 미션을, 이제는 TDD로 다시 끝까지 완주했다는 사실 자체가 이번 여정의 의미를 잘 보여주는 것 같다. 예전에는 제출 전날 보너스 번호 버그를 뒤늦게 발견하고, 요구사항을 놓친 채 설계 고민에만 빠져 있었지만, 이번에는 같은 미션을 &amp;ldquo;테스트를 먼저 두고, 설계와 구현을 그 위에 쌓아 올리는 방식&amp;rdquo;으로 다시 걸어갔다.&lt;/p&gt;
&lt;p data-end=&quot;399&quot; data-start=&quot;220&quot; data-ke-size=&quot;size16&quot;&gt;선정 배경에서 내가 세웠던 질문들&amp;nbsp; &amp;ldquo;왜 테스트를 했는데도 버그가 늦게 발견됐을까?&amp;rdquo;, &amp;ldquo;왜 리팩토링이 이렇게 불안했을까?&amp;rdquo;, &amp;ldquo;왜 테스트가 설계를 도와주지 못했을까?&amp;rdquo; 모두 이번 1~3차 TDD 회고와 3주차 로또 미션의 경험들은 그 질문들에 대한 부분적인, 그러나 분명한 답을 보여줬다고 느낀다. 처음 세웠던 목표들 역시 하나씩 몸으로 확인해 가는 과정이었다.&lt;/p&gt;
&lt;p data-end=&quot;1148&quot; data-start=&quot;993&quot; data-ke-size=&quot;size16&quot;&gt;물론, 여전히 나는 &amp;ldquo;설계 피드백 도구로서의 TDD&amp;rdquo;를 완전히 자기 것으로 만들었다고 말하긴 어려운것 같다 분명히 너무나도 해당 도구를 잘 모쓰고 있는것 같은 느낌이 든다&amp;nbsp;&lt;br /&gt;책 한 권 읽고, 이론 한 줄 안다고 되는 게 아니라는 것도 충분히 느꼈다.&lt;br /&gt;하지만 분명한 건, 이제 테스트는 단순히 정답을 확인하는 마지막 체크리스트가 아니라, &lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;요구사항을 더 정확히 보게 만들고, &lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;잘못된 가정을 조기에 드러내 주고, &lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;설계를 조금 더 나은 방향으로 밀어 올려 주고, &lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;리팩토링을 겁내지 않게 해 주는 &lt;/span&gt;그런 &amp;ldquo;생각의 프레임&amp;rdquo;이 되기 시작했다는 점이다 !!&lt;/p&gt;
&lt;p data-end=&quot;1397&quot; data-start=&quot;1278&quot; data-ke-size=&quot;size16&quot;&gt;이번 3주차 로또 미션 TDD 경험은 내가 세웠던 목표들 오해를 깨고, 진실을 체득하고 싶다에 완벽히 도달한 것은 아니지만, 적어도 그 방향이 어디인지는 몸으로 알게 된 단계라고 느낀다.&lt;/p&gt;
&lt;p data-end=&quot;1481&quot; data-start=&quot;1399&quot; data-ke-size=&quot;size16&quot;&gt;이제 남은 건, 더 많은 미션, 더 다양한 도메인, 더 복잡한 현실 속에서 이 기준을 계속 확인하고, 부수고, 다시 세워 가는 일일 것이다.&lt;/p&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;1530&quot; data-start=&quot;1483&quot; data-ke-size=&quot;size16&quot;&gt;그리고 그 과정을, 나는 앞으로도 테스트 코드 위에 하나씩 기록해 나갈 생각이다 !&lt;/p&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;1530&quot; data-start=&quot;1483&quot; data-ke-size=&quot;size16&quot;&gt;자 이제 마지막 대망의 요구사항 폭탄의 편의점 미션만 남았다..! 작년 그토록 복잡하고 어려워 많이 갈아엎으며 고통을 받았던 경험들이 TDD와 함께라면 또 어떤 경험을 줄지 너무 기대된다 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>외부활동/우아한테크코스 [프리코스]</category>
      <author>softmoca__</author>
      <guid isPermaLink="true">https://softmoca.tistory.com/560</guid>
      <comments>https://softmoca.tistory.com/560#entry560comment</comments>
      <pubDate>Wed, 19 Nov 2025 11:27:57 +0900</pubDate>
    </item>
    <item>
      <title>테스트 주도개발 TDD 실천법과 도구 정리 도서 요약[FAQ &amp;amp; 설계사고과정]</title>
      <link>https://softmoca.tistory.com/555</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000001223650&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://product.kyobobook.co.kr/detail/S000001223650&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1764216498735&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;테스트 주도 개발 TDD 실천법과 도구 | 채수원 - 교보문고&quot; data-og-description=&quot;테스트 주도 개발 TDD 실천법과 도구 | 효율적인 설계와 간결한 코드를 만드는 필수 TDD 기법『TDD 실천법과 도구』는 업고픔질 소프트웨어를 만드는 유쾌한 개발 비법 TDD를 다룬 책이다. 초급 개&quot; data-og-host=&quot;product.kyobobook.co.kr&quot; data-og-source-url=&quot;https://product.kyobobook.co.kr/detail/S000001223650&quot; data-og-url=&quot;https://product.kyobobook.co.kr/detail/S000001223650&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/br7JgX/hyZOyCiWsX/XO2E56NaqaTKg9BzyEKLNk/img.jpg?width=458&amp;amp;height=571&amp;amp;face=0_0_458_571,https://scrap.kakaocdn.net/dn/ujBUB/hyZOEBKaOl/wGCNvwXF6P1Q30Q3dZOqn0/img.jpg?width=458&amp;amp;height=571&amp;amp;face=0_0_458_571,https://scrap.kakaocdn.net/dn/v1LRW/hyZOu0Zdvp/OZ4SFpNocgCwaJ3JK01Um1/img.png?width=335&amp;amp;height=335&amp;amp;face=0_0_335_335&quot;&gt;&lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000001223650&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://product.kyobobook.co.kr/detail/S000001223650&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/br7JgX/hyZOyCiWsX/XO2E56NaqaTKg9BzyEKLNk/img.jpg?width=458&amp;amp;height=571&amp;amp;face=0_0_458_571,https://scrap.kakaocdn.net/dn/ujBUB/hyZOEBKaOl/wGCNvwXF6P1Q30Q3dZOqn0/img.jpg?width=458&amp;amp;height=571&amp;amp;face=0_0_458_571,https://scrap.kakaocdn.net/dn/v1LRW/hyZOu0Zdvp/OZ4SFpNocgCwaJ3JK01Um1/img.png?width=335&amp;amp;height=335&amp;amp;face=0_0_335_335');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;테스트 주도 개발 TDD 실천법과 도구 | 채수원 - 교보문고&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;테스트 주도 개발 TDD 실천법과 도구 | 효율적인 설계와 간결한 코드를 만드는 필수 TDD 기법『TDD 실천법과 도구』는 업고픔질 소프트웨어를 만드는 유쾌한 개발 비법 TDD를 다룬 책이다. 초급 개&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;product.kyobobook.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Q.private 메소드도 테스트 케이스를 만들어야 하나요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 먼저'Private 메소드가 왜 생기나?' 혹은 언제 private 메소드를 만들게 되나?'를 생각해볼 필요가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java에서의 private은 접근 범위(access scope)로 봤을 때 오직 자신의 클래스 내에서만 접근 가능한 속성이나 메소드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 외부에 알리고 싶지 않은 부분이라는 전제하에 만들어집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 클래스의 효용 가치는 그 클래 스가 갖고 있는 정보와 그 정보를 기반으로, 다른 클래스와 상호작용한다는 데 있다. 이를 모듈화, 독립화라고도 하는데, 다른 클래스 입장에서는 자신이 원하는 기능을 올바로 해주기만 하면 더 이상 신경 쓰지 않아도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 그 외부에 제공 하는 기능이 제대로 동작하는지 보장해준다는 의미로 테스트 케이스를 만드는 것이다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 자동차를 살 때 특정 전선들이 정상 동작하는지, 모터의 회전수가 일정 수준 이 상으로 유지되는지를 확인하지 않습니다. 자동차를 이용하는 사용자 측면에서는 헤드라이트가 잘 커지고 꺼지고, 핸들이 정확히 동작하는지를 테스트해보고, 만족하면 되 는 거다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 이 문제에 대해서는 많은 사람이 다양하게 논의를 해왔 고, 근래에는 private 메소드는 테스트하지 않아도 무방하다는 것이 주류이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리 고 public 메소드를 테스트하면 그에 딸린 private 메소드는 자연스럽게 테스트되었다 고 간주한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 굳이 private 메소드에 대한 테스트 케이스 수행을 원한다면 리플 레션 등을 이용할 수는 있지만, 리플렉션이라는 기술의 특성상 유리처럼 부서지기 쉬운 테스트 코드가 될 가능성이 크다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Q 사용자가 직접 수행하는 수동 테스트와 자동화된 테스트, 굳이 TDD에 한정하지 않고 봤을 때, 어느 것이 더 중요한가요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TDD의 가치를 크게 두 개만 꼽는다면, 지속적인 설계 개선과 자동화된 테스트라고 할 수 있다. 자동화는 우리가 지향해야 하는 목표가 맞다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 자동화는 대부 분의 경우에 장점이 되지만 모든 경우에 무조건 그런 건 아니다. 우선, 자동화하는 데는 많은 노력이 들어간다. 자동화의 비율이 높을수록 비용이 많이 든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 자동화된 테스트를 작성하고자 할 때는 ROI를 고려해야 한다. 당장 오픈이 낼 모래 인데, 테스트 자동화를 위해 노력하는 건 어리석은 일일 수 있다. 하지만 자동화는 확실히 다양한 이점을 제공하며 특히 오랜 기간을 놓고 볼 때는 더욱 그렇습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수동 테스트의 단점이 자동화된 테스트의 장점인 경우가 대부분이므로, 반대로 수동 테스트의 단점을 살펴보면 이렇다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫째, 회귀 테스트가 팀에 큰 부담이된다. 개발자가 기능을 추가/변경할 때마다 매 번 기능 테스트를 수행해야 하는데, 누락되거나 지나치게 단순화된 형태로만 테스트를 진행하게 될 가능성이 크다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘째, 실패/성공, 테스트 커버리지 등 다양한 보고서를 손쉽게 볼 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론은 적절한 수준에서의 자동화 테스트를 결정해야 한다. 때로는 전체 자동화(fall automation)가 불가능할 경우가 있다. 그렇다고 자동화를 포기하지는 마라.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Q. TDD를 적용하기 어려운 대표적인 이유는 무엇입니까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TDD는 이름처럼 테스트가 주도적으로 개발을 이끌어나가는 것이 목표이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작성해야 할 기능(ToDo)이 있으면 코드를 작성하기 전에 테스트 케이스를 작성해서 진행하는 것이 일반적인 순서이다. 그런데 문제는 해야 할 기능이나 기능 리스트가 명확하지 않으면 테스트 코드를 작성하기가 매우 어려워진다는 점이다. 실제로 TDD가 너무 어렵다며 포기한 사례가 많다. 물론 TDD 튜 토리얼에서 제시하는 코드들이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 예제 코드의 수준이 현업의 업무 장 황이나 레벨과 차이가 나는 경우가 많다. 또한 개발자들의 연습이 되어 있지 않 다는 점도 큰 이유이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 TDD를 효과적으로 적용하기 위한 체계적인 가이드 가 없다는 것도 큰 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어찌어찌 어렵게 테스트 코드를 작성해나가지만, 생각 보다 꽤 고통스러운 과정이라고 느끼게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 결과적으로 이를 통해 만들어 진 코드들이 어지럽게 펼쳐져서는 한곳으로 모이지 못하고, 즉 TDD가 지향하는 설계 의 개선으로 흐름이 이뤄지지 않고, 결국 TDD를 포기해버리는(진행이 붕괴돼버리는, 그래서 전통적인 개발 방식으로 다시 회귀해버리는) 케이스도 적지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 일 부 사람들은 TDD를 하면, 설계를 하지 않고 테스트 케이스 작성과 리팩토링을 통해 설계를 만들어간다고 너무 강력하게 믿기에 요구사항 그 자체를 ToDo로 보고 무작정 TDD를 시작하는 경우도 있다. 그런데 TDD 본래의 지향점은 설계를 하지 않는 데 있진 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완벽한 설계가 이뤄진 다음 개발에 들어가는 것이 아니라 설계를 일부 먼저 하고, 그 이후에는 TDD를 통해 불완전한 설계 구조를 지속적으로 개선해나가는 방식을 취한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로는 고객의 다양한 요구사항을 필요한 수준에서 어렵지 않 게 만족시킬 수 있는 더 나은 구조로 설계 모습을 갖게 되는 거다. 따라서 선행 설계는 반드시 필요하다. 다만, 그 깊이와 목표가 전통적인 방법론에서 이야기하는 내용과는 다소 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애자일에서는 설계는 하되, 노력 대비 효용이 높 은 최소 비용 설계'를 지향합니다. 그럼, 여기서 말하는 최소 비용 설계란 무엇일까? 또는 어떻게 해야 할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설계의 첫 시작은 우선 요구사항을 정련하는 것에서부터 시작해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음에 종이가 됐든, 설계 프로그램이 됐든, 참여한 개발자들이 의견일치(Concensus)할 수 있 도록, 사용자 스토리를 만족시킬 수 있는 최소한의 개발 구조(설계, 디자인)를 도출하 고, 그 구조를 바탕으로 정련된 요구사항에서 ToDo를 발췌해나간다. 그 이후 각각 의 TODo에 대응하는 테스트를 하나씩 작성하고 만족시키는 식으로 개발을 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서. (정말 안타깝지만) TDD는 개발 언어 사용기술만 갖고서는 원활한 진행이 다소 어려울 수 있다(불가능한 건 아니지만, 다시 앞 단계로 돌아가서 설계를 고치 는 일이 반복적으로 생기게 된다).&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요구사항 도출 정련 기법과 기본 설계 개념에 대 해서는 미리 어느 정도 이상의 지식이 있어야 한다. 이러한 사실이 다소 부담이 될 순 있지만, 고무적인 일은, 개발 1년차 개발자와 함께 요구사항을 정련해서 간략한 설 계를 도출한 다음 ToDo를 찾아내서 시작할 수 있도록 유도하는 작업을 했던 적이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자가 개발에 어느 정도 감이 있었기에, 요구사항 도출과 정련, 그리고 개 략적인 설계에 대한 몇 가지 기본 원칙과 방법'을 전달해준 것만으로도, 두어 시간 만 에 해당 개발자의 개발 방식이 크게 향상될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 막막해하던 개발 자가 ToDo 리스트에서 무엇부터 할지를 선택해 TDD를 진행하며 스스로 어떤 부분이 잘못 돼가는 것 같다는 판단과 함께, 이후 어떻게 진행할 것인지 생각하면서 개발할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 말해, 짧은 시간의 도움만으로도 TDD를 할 수 있다는 자신감과 함께 개발 진행 스타일에 대해 어느 정도나마 감을 잡을 수 있게 됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Q 테스트 케이스를 최대한 정교하게 작성하려고 노력하고 있습니다. 그런데, 그러다 보니 테 스트 대상 객체나 코드가 조금만 변경이 일어나도 테스트가 와장창 깨져서 유지보수하는 데 비 용이 많이 듭니다. 이젠 솔직히 TDD에 대한 회의가 들 정도에요. 뭐가 잘못된 걸까요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;민감한 센서일수록 오탐(false postve)불이 더 높은 게 보통이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실, 질문하신 내용이 TDD의 어떤 한계점이라고 이야기하는 사람들도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아쉽지만, 뾰족한 답은 없다. 다만, 모든 테스트 케이스를 다 정교히 작성하기보다는, 중요하거나 민 감하게 반응해야 하는 모듈에 대해서만 정교한 테스트 케이스를 작성하는 것도 하나의 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확히 지키긴 어렵지만, 딱, 필요한 만큼만 테스트한다'는 원칙을 세워보 면 어떨까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Q 면접 시에 TDD에 대해 물어보는 건 어떻게 생각하시나요?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;만일 제가 면접관이라면 &quot;TDD를 해본 적이 있습니까? 있다면 장점과 단점은 무엇이 라고 생각합니까?'라고 물어볼 것 같다.&lt;/li&gt;
&lt;li&gt;단순히 관심은 있습니다 보다는 &quot;적용해 본 적이 있습니다&quot; 쪽이 실력은 둘째 치고라도 개발에 대해 더 열의가 있는 편이라고 생각할 수 있을 것 같다.&lt;/li&gt;
&lt;li&gt;그리고 TDD를 안다고 말한다면, 꼭 TDD의 단점에 대해 물어보자. 대답은 뭐가 되어도 괜찮다.&lt;/li&gt;
&lt;li&gt;단점을 이야기하는 게 장점을 물어보는 것보다는 더 면접자의 진실을 알려줄 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Q 팀에 TDD를 도입하고자 할 때 초반에 TDD 외에 다른 어떤 활동을 같이 하면 좋을까요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TDD를 도입하면 초반에 개발자들이 익숙해질 때까지는 다 함께 코드 리뷰를 진행할 것을 권장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작성 시 유의점과 어려움에 대해 함께 이야기하는 것으로 팀원들이 TDD를 진행하는 데 어려움을 줄여줄 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 새로운 기능을 추가하거나 버그를 찾을 때 테스트 케이스를 먼저 만들도록 어느 정도 강제화할 필요가 있다. TDD는 습관이 중요하므로, 한두 번은 넘어가는 식으 로 체계가 무너지면 곤란하다. 어느 정도 TDD가 익숙해지면 CI 서버(지속적인 통합 서버) 등을 함께 사용할 것을 권장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Q void 메소드를 테스트 가능하게 만들려면 어떻게 해야 할까요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 void 메소드가 언제 사용되는지에 대해 먼저 고민해봐야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 흔히 리 턴값이 없는 메소드를 Void라고 생각하고 개발을 하는 경우가 많지만, 엄밀히 따지면 이렇다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리턴값이 있는 메소드: 기능(function)&lt;/li&gt;
&lt;li&gt;void 메소드: 절차(procedure)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메소드의 리턴값이 없다는 의미는 로직의 절차를 기술했거나, 프로그램 로직 트리의 맨 끝단에 해당하는 기능 위임 작업(이를테면 화면 출력, 데이터 등록 같은 작업)을 목 표로 하겠다는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 해당 void 메소드의 이름이 의미하는 작업을 그 메소드 에서 끝을 내겠다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 메소드는 Mock 객체를 설명했던 부분에서 이야기 했던, 행위 기반 테스트를 해야 한다. 가장 간단한 방법은, 상태(state)를 확인할 수 있는 다른 메소드로 결과를 테스트하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만일 상태를 확인할 인터페이스10 를 제공받지 못하는 상황이라면, 리플렉션 같은 방법을 사용할 수도 있다. 하지만 그다지 권장하진 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누누히 이야기 하지만 리플렉션으로 작성된 테스트 코드 는 특히나 깨지기 쉬운 테스트가 돼버리곤 하기 때문이다. 가급적이면 상태를 확인 할 수 있는 isClicked(), isReady) 같은 상태확인 인터페이스를 만들어줘라.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아니면 Mock 프레임워크를 사용할 수도 있다. 다시 한번 강조하지만, 테스트 가능한 설계가 곧 좋은 설계라는 걸 잊지 말아야한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;TODO 리스트, 대략적인 설계와 기능 리스트 작성 방법 사고 과정 예시&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 해당 도메인의 대략적인 업무 시나리오는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고객에게 비디오 타이틀 을 대여하고 대여한 타이틀에 대한 요금을 알려준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비디오 가게에서는 포인트 제도 를 운영 중이며, 영화 종류와 대여기간에 따라 포인트가 조금 다르다. 구현해야 하는 기능을 중심으로 요구사항을 상세히 발췌해봤더니 다음과 같았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목적&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비디오 가게에서 고객이 대여하는 비디오의 대여정보를 조회할 수 있는 프로그램을 작성한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요구사항 ① 고객은 이름을 갖는다. ② 고객은 한 번에 여러 개의 비디오를 대여할 수 있으나 각각의 대여기간은 다를 수 있다. ③ 비디오는 영화, 스포츠, 다큐멘터리의 세 종류가 있다. ④ 각 비디오는 독립적인 일일 대여요금을 갖는다. ⑤ 영화는 대여기간이 2일 이상 되면 3일째부터는 대여요금이 12로 할인된다. ⑥ 다큐멘터리는 3일 이상 대여하면 4일째부터는 1/3로 할인된다. ⑦ 스포츠는 장기대여 할인이 없다. ⑧ 비디오 1개 대여할 때마다 보너스 포인트는 1포인트씩 올라간다. 단, 스포츠는 2포인트씩 올라간다. ⑨ 과거의 대여기록을 보유하고 있을 필요는 없으나 고객이 얻은 총 보너스 포인트 정보는 알 고 있어야 한다. 10 고객의 현재 대여정보를 구할 수 있는 기능을 작성하라.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;총 대여비디오 수&lt;/li&gt;
&lt;li&gt;대여정보 : 비디오(종류,제목, 가격), 대여기간 리슽으&lt;/li&gt;
&lt;li&gt;총대여 가격&lt;/li&gt;
&lt;li&gt;현재 대여하고 있는 비디오로 인해 추가된 포인트&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 개발자가 이런 요구사항을 받아본 다음에 별로 복잡하지 않다는 느낌이 오면, 바로 에디터 창을 띄워 개발에 들어가려는 경향이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그건, 경험 많은 개발자에게조차 그다지 권장할 만한 방법은 아니다. 이 예제 같 은 경우엔 사용자 시나리오도 시나리오지만, 그전에 요구사항 정련이 먼저 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 기본 규칙은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나의 목적을 갖는 의미나 동작으로 최대한 간결히 표현한다.&lt;/li&gt;
&lt;li&gt;'업무규칙'과 '기능 요구사항'을 분리한다.&lt;/li&gt;
&lt;li&gt;'고유명사는 대명사로, 명시적인 숫자는 변수로' 참조할 수 있도록 표시해두면 도움이 된다. 이때 종류 중 하나를 나타낼 때는 어떤(any)으로 치환해 표현하는 것도 때로 도움이 된다.&lt;/li&gt;
&lt;li&gt;행동의 주체를 가급적 명확히 표현한다.&lt;/li&gt;
&lt;li&gt;동작과 소유를 분리한다. 예: 객체의 동작인가? 객체의 소유인가?&lt;/li&gt;
&lt;li&gt;복합 한자어는 유심히 봐서 분리 가능한지 확인해서 풀어쓰면 더 좋다. 예: 대여요금- 대여행위 시에 발생하는 요금&lt;/li&gt;
&lt;li&gt;상호작용을 유심히 관찰한다. 예: 시스템의 행위인가? 고객의 행위인가?&lt;/li&gt;
&lt;li&gt;구어체는 좀 더 명확한 의미를 가진 문어체로 변경한다. 예: 구한다 - 화면에 보여준다. 계산한다. 저장한다 올라간다- 증가한다. 누적된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 기반으로 요구사항을 정련하는 모습을 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요구사항 항목별로 업무규칙인지 기능 요구사항인지 구분해 표기하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;번호가 없는 항목들은 정련 작업 없이 그대로 사용할 생각이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고객은 한 번에 여러 개의 비디오를 대여할 수 있으나 각 대여기간은 다를 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;업무규칙 *&lt;/li&gt;
&lt;li&gt;고객은 한 번에 여러 개의 비디오를 대여할 수 있다.&lt;/li&gt;
&lt;li&gt;대여기간은 비디오마다 각각 다를 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비디오는 영화, 스포츠, 다큐멘터리의 세 종류가 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;업무규칙 *&lt;/li&gt;
&lt;li&gt;비디오에는 종류가 있다.&lt;/li&gt;
&lt;li&gt;현재 비디오의 종류는 영화, 스포츠, 다큐멘터리의 세 종류다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영화는 대여기간이 2일 이상 되면 3일째부터는 대여요금이 1/2로 할인된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;업무규칙 *&lt;/li&gt;
&lt;li&gt;영화는 대여기간이 n일 이상이 되면 m일째부터는 대여요금이 p로 할인된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 식으로 요구사항을 상세화해서 다시 적어보면 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;업무규칙 *&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;고객은 이름을 갖는다.&lt;/li&gt;
&lt;li&gt;고객은 한 번에 여러 개의 비디오를 대여할 수 있다.&lt;/li&gt;
&lt;li&gt;대여기간은 비디오마다 각각 다를 수 있다.&lt;/li&gt;
&lt;li&gt;비디오에는 종류가 있다.&lt;/li&gt;
&lt;li&gt;현재 비디오의 종류는 영화, 스포츠, 다큐멘터리의 세 종류다.&lt;/li&gt;
&lt;li&gt;각 비디오는 독립적인 일일 대여요금을 갖는다.&lt;/li&gt;
&lt;li&gt;영화는 대여기간이 2일 이상 되면 3일째부터는 대여요금이 1/2로 할인된다. 예: 영화는 대여기간이 n일 이상이 되면 m일째부터는 대여요금이 p로 할인된다.&lt;/li&gt;
&lt;li&gt;다큐멘터리는 3일 이상 대여하면 4일째부터는 1/3로 할인된다. 예: 어떤 비디오의 타입은 n일 이상 대여하면 m일째부터는 p로 할인된다.&lt;/li&gt;
&lt;li&gt;스포츠는 장기대여 할인이 없다. 예: 어떤 비디오 타입은 장기대여 할인이 없다.&lt;/li&gt;
&lt;li&gt;비디오 1개 대여할 때마다 보너스 포인트는 1포인트씩 누적된다(단, 스포츠는 2포인트씩). 예: 고객의 보너스 포인트 누적점수는 비디오를 n개 대여할 때마다 보너스 포인트는 m포 인트씩 누적된다(단, 특정 타입은 p포인트씩).&lt;/li&gt;
&lt;li&gt;시스템은 고객의 과거의 대여기록을 보유하고 있을 필요 없다.&lt;/li&gt;
&lt;/ol&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;고객과 협의해 확정해야 하는 업무규칙 *
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대여는 일 단위다.&lt;/li&gt;
&lt;li&gt;포인트는 정수다.&lt;/li&gt;
&lt;li&gt;할인은 분수 비율다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;기능 요구사항 *&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;고객은 비디오를 대여할 수 있다.&lt;/li&gt;
&lt;li&gt;시스템은 고객의 현재 대여정보를 제공할 수 있다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;대여정보
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;총 대여비디오 수&lt;/li&gt;
&lt;li&gt;비디오(종류 + 제목 + 가격), 대여기간 리스트&lt;/li&gt;
&lt;li&gt;총 대여가격&lt;/li&gt;
&lt;li&gt;현재 대여하고 있는 비디오로 인해 추가된 포인트&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;시스템은 현 대여로 인해 발생하는 포인트 총합을 계산할 수 있다.&lt;/li&gt;
&lt;li&gt;시스템은 대여된 비디오들의 총 대여가격을 계산할 수 있다.&lt;/li&gt;
&lt;li&gt;시스템은 총 대여비디오 수를 계산할 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기타 요구사항 확인 작업 *
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;과거의 대여기록을 보유하고 있을 필요는 없으나 고객이 얻은 총 보너스 포인트 정보는 알고 있어야 한다. &amp;rarr; 포인트 정보의 기원을 의미하는 것지, 개별 포인트만의 관리가 아닌 대여 반납의 포인트는 할인가지를 확인한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자. 이번엔 사용자 시나리오를 작성해 그걸 기반으로 테스트 케이스를 만드는 식으로 진행하는 것이 아니라, 클래스를 먼저 도출해보는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자. 개발에 편한 클래스, 즉 행위 기반으로 요구사항을 목록으로부터 읽고 도출해보자. 크게 행위 기반 클래스와 소유 기반 클래스로 나뉘었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;행위 기반 클래스 도출&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대여한다 (고객이 비디오를)&lt;/li&gt;
&lt;li&gt;할인된다 (비디오의 일일 대여가격이)&lt;/li&gt;
&lt;li&gt;누적된다 (보너스 포인트가)&lt;/li&gt;
&lt;li&gt;계산한다 (시스템이 포인트 총합을)&lt;/li&gt;
&lt;li&gt;계산한다 (시스템이 총 대여가격을)&lt;/li&gt;
&lt;li&gt;계산한다 (시스템이 총 대여비디오 수를)&lt;/li&gt;
&lt;li&gt;제공한다 (시스템이 대여정보를)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제, 이 정도면 됐다면서 구현에 들어가고 싶은 마음이 굴뚝같을 텐데 조금만 더 참자. 아직 보정이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 쉬운 것부터&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OOP에서 객체에 수동 액션을 담는 일은 좀처럼 없다(버스가 운전을 당한다? 설마&amp;hellip;). 그런데 요구사항으로부터 액션(행동)을 도출하고 보면 본의 아니게 수동이 쓰이는 경우가 있다. 만일 수동이 쓰인 상태로 구현을 하게 되면 타 모듈에 대한 의존성이 높아져서 설계가 전체적으로 복잡해진다. 다음 예를 살펴보자.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;할인된다 (비디오 일일 대여가격이)&lt;/li&gt;
&lt;li&gt;누적된다 (보너스 포인트가)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 두 가지 액션을 하는 행위를 능동으로 바꿔보자. 바꾸다 보면, 행위의 주체가 시스템이라는 걸 자연스럽게 알 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;할인한다 (시스템이 비디오 일일 대여가격을)&lt;/li&gt;
&lt;li&gt;누적한다 (시스템이 포인트를)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번엔 살짝 복잡한 걸로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;언제나 느끼지만 요구사항의 한자어는 위험하다. &amp;lsquo;대여한다&amp;rsquo;는 말은 사실 매우 어려운 말이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무언가를 가져와서 일정 기간 동안 소유로 삼다가 돌려준다는 뜻인데, 요구사항을 보면, 여러 개를 대여할 수 있고, 항목별 대여일수가 다를 수 있으며, 대여한 일수에 따라 대여 항목별로 비용과 포인트가 각각 책정되었다고 적혀있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 지원하기 위해서는 각 대여항목을 독립적으로 다룰 무언가가 필요하다. 클래스를 도출하기 위해 명사를 기준으로 분석해보면 &amp;lsquo;대여&amp;rsquo;라는 실체가 없는 개념이 넘어 필요해진다. 대여라는 행위를 하나의 추상 개념으로 구현할 경우 고객과 영화를 직접 다루는 &amp;lsquo;고객 &amp;ndash; 영화&amp;rsquo; 식으로 설계한 경우에는, 이러한 대여항목별 대여일자 정보를 같이 다루기가 어려워진다. 따라서 다소 떠한 결론적으로, 대여에는 영화를 포함하는 식의 &amp;lsquo;고객 &amp;ndash; 대여 &amp;ndash; 영화&amp;rsquo; 같은 구성을 갖게 되면, 고객:대여=1:m , 대여:영화=1:1 관계를 통해 대여일수 관리가 가능해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 소유 기반으로 클래스를 도출한 부분이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소유 기반 클래스 도출&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이름을 갖는다 (각 고객은)&lt;/li&gt;
&lt;li&gt;종류를 갖는다 (각 비디오는)&lt;/li&gt;
&lt;li&gt;대여요금을 갖는다 (각 비디오는)&lt;/li&gt;
&lt;li&gt;포인트를 갖는다 (각 비디오)&lt;/li&gt;
&lt;li&gt;대여일수를 갖는다 (비디오인가? 고객이?)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마찬가지로 정련 작업을 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 쉬운 것부터&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TDD에서는 크게 강조하는 내용에 해당하진 않지만, 소유 기반 클래스를 살펴볼 때는 소유의 기준이 되는 조건이 무엇인지 생각해볼 필요는 있다. 예를 들면, 포인트 정보를 갖고 있어야 하는 것은 대여한 비디오(클래스)인가? 아니면, 대여한 비디오의 타입인가? 비디오가 포인트를 갖는다고 생각하기 쉽지만, 사실 부여되는 포인트는 비디오가 어떤 타입을 갖느냐에 달려 있다. 스포츠 타입인가? 아니면 다큐멘터리 타입인가? 하는 식으로 말이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번엔 살짝 어려운 걸로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대여일수를 갖는다 (비디오가? 고객이?)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대여일수 정보는 어디에 둘 것인가에 대한 고민이다. 어느 쪽(클래스)에 놓을지 애매할 경우에는 객체의 구체적인 인스턴스를 예로 들어서 해당 정보를 &amp;lsquo;갖고 있다&amp;rsquo;는 말이 적합한지 테스트해본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예: 타이타닉은 대여일수를 갖고 있다. (타이타닉의 대여일이 고정?)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장동건은 대여일수를 갖고 있다. (장동건은 7일? 무슨 소리냐 이건?)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 식으로 따져보면, 비디오도, 고객도 대여일수는 갖는 모습이 어색하다는 걸 알 수 있다. 앞서 찾아낸 &amp;lsquo;대여&amp;rsquo;라는 추상적인 개념이 이 부분에서 쓰이는 것이 적절해보인다. 그리고 이때 클래스에 해당 정보가 위치해야 하는 이유를 함께 생각해볼 필요가 있다. 예를 들자면, &amp;lsquo;대여일수를 갖는다&amp;rsquo; &amp;rarr; 대여일수를 대체 왜 필요하고 하는가? &amp;rarr; 대여가격과 포인트를 계산하려고! 하는 식으로 말이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예: 타이타닉 비디오 영화에 대한 &amp;lsquo;대여&amp;rsquo;는 대여일수를 갖고 있다. (비용과 포인트 계산이 가능!)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 식으로 표현해보면, 영화와 대여가 1:1 관계가 된다는 것도 문장에서 자연스럽게 파악된다. 이 정도까지 발전하면 이제 &amp;lsquo;대여일수를 갖는다&amp;rsquo; 주체와 그 클래스를 해야 하는 행동이 눈에 보이게 된다. 이렇게 해서, 최소의 문장을 정련한 모습은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대여일수를 갖는다 (각 대여는)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이쯤 되면 사람이 따라 &amp;ldquo;TDD는 이런 게 아니라고! 설계를 해버렸잖아!&amp;rdquo;라고 말하고 싶을 수도 있다. 아쉽지만, 그건 또 그렇지가 않다. TDD에 들어가는 시험에 의해 최소한의 클래스와 시스템의 행위는 나와 있어야 한다. XP가 극단을 추구했기에 eXtreme이라고 불리지만, 창시자 켄트 벡도, 그의 절친 마틴 파울러 아저씨도 필요할 만큼의 설계는 개발 전에 해야 한다고 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 정확히는 TDD란 말 자체도, &amp;lsquo;시스템 구축을 개발부터&amp;rsquo;의 의미를 나타내는 것이 아니라, &amp;lsquo;개발할 때는 테스트 코드부터(Test First Development)&amp;rsquo;의 의미를 갖는다. 약 설명들은, 글로 설명하느라고 더 길고 복잡해진 것이지, 사실 두 명 정도의 인원이 요구사항을 같이 읽고 바로 정련에 들어갔다면 20~30분 내외에서 TDD를 시작할 수 있는 수준이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 내용을 다 따라서 최종적으로 정리해보면 다음과 같이 클래스가 도출된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;행위 기반 클래스&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대여한다 (고객이 비디오를)&lt;/li&gt;
&lt;li&gt;할인한다 (시스템이 비디오 일일 대여가격을)&lt;/li&gt;
&lt;li&gt;누적한다 (시스템이 포인트를)&lt;/li&gt;
&lt;li&gt;계산한다 (시스템이 포인트 총합을)&lt;/li&gt;
&lt;li&gt;계산한다 (시스템이 총 대여가격을)&lt;/li&gt;
&lt;li&gt;계산한다 (시스템이 총 대여비디오 수를)&lt;/li&gt;
&lt;li&gt;제공한다 (시스템이 대여정보를)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소유 기반 클래스&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이름을 갖는다 (각 고객은)&lt;/li&gt;
&lt;li&gt;종류를 갖는다 (각 비디오는)&lt;/li&gt;
&lt;li&gt;대여요금을 갖는다 (각 비디오는)&lt;/li&gt;
&lt;li&gt;포인트를 갖는다 (각 비디오 타입은)&lt;/li&gt;
&lt;li&gt;대여일수를 갖는다 (각 대여는)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 이 정도 수준의 요구사항 정제만으로 만들어진 위와 같은 설계가 절대 완벽할 리는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 개발의 시작점은 마련해줄 수 있기 때문에, TDD로 개발해나가면서 리팩토링과 함께 설계를 개선하는 방향을 통해 충분히 보완할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 각 클래스에 대한 테스트 케이스를 만드는 식으로 구현에 들어가면 좀 더 쉽게 작업할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 시작은 소유 기반 클래스부터, 그리고 다른 클래스로의 확인이 낮은 클래스부터 시작하는 걸 권장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런 클래스들은 자신만으로 테스트 케이스 작성이 가능하기 때문에 테스트 케이스 작성의 난이도가 비교적 낮다. 위의 클래스 다이어그램 기준으로는 비디오 타입 클래스나 비디오 클래스부터 하는 것을 말한다. 행위 기반 클래스들은 이런저런 클래스들을 함께 이용해야 하는 경우가 많다. 시스템에 해당하는 VideoShop 클래스를 보면, 기능(=행위)이 잔뜩 포진해 있기 때문에, 기능을 구현할 때 많은 클래스가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 일반적인 상황이라면, VideoShop 클래스에 대한 TDD 개발은 다소 늦추면서 이런저런 가능성이 크다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 정도 설계를 한 후, 소유 기반 클래스부터 시작해 행위 기반 클래스까지 차분히 테스트 케이스를 만들면서 개발해나가다 보면, 최종적으로는 통합 테스트(integration test) 수준의 테스트 케이스가 자연스럽게 만들어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>외부활동/우아한테크코스 [프리코스]</category>
      <author>softmoca__</author>
      <guid isPermaLink="true">https://softmoca.tistory.com/555</guid>
      <comments>https://softmoca.tistory.com/555#entry555comment</comments>
      <pubDate>Thu, 13 Nov 2025 21:35:33 +0900</pubDate>
    </item>
    <item>
      <title>테스트 주도개발 TDD 실천법과 도구 정리 도서 [핵심 요약]</title>
      <link>https://softmoca.tistory.com/554</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000001223650&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://product.kyobobook.co.kr/detail/S000001223650&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1764216542974&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;테스트 주도 개발 TDD 실천법과 도구 | 채수원 - 교보문고&quot; data-og-description=&quot;테스트 주도 개발 TDD 실천법과 도구 | 효율적인 설계와 간결한 코드를 만드는 필수 TDD 기법『TDD 실천법과 도구』는 업고픔질 소프트웨어를 만드는 유쾌한 개발 비법 TDD를 다룬 책이다. 초급 개&quot; data-og-host=&quot;product.kyobobook.co.kr&quot; data-og-source-url=&quot;https://product.kyobobook.co.kr/detail/S000001223650&quot; data-og-url=&quot;https://product.kyobobook.co.kr/detail/S000001223650&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/br7JgX/hyZOyCiWsX/XO2E56NaqaTKg9BzyEKLNk/img.jpg?width=458&amp;amp;height=571&amp;amp;face=0_0_458_571,https://scrap.kakaocdn.net/dn/ujBUB/hyZOEBKaOl/wGCNvwXF6P1Q30Q3dZOqn0/img.jpg?width=458&amp;amp;height=571&amp;amp;face=0_0_458_571,https://scrap.kakaocdn.net/dn/v1LRW/hyZOu0Zdvp/OZ4SFpNocgCwaJ3JK01Um1/img.png?width=335&amp;amp;height=335&amp;amp;face=0_0_335_335&quot;&gt;&lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000001223650&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://product.kyobobook.co.kr/detail/S000001223650&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/br7JgX/hyZOyCiWsX/XO2E56NaqaTKg9BzyEKLNk/img.jpg?width=458&amp;amp;height=571&amp;amp;face=0_0_458_571,https://scrap.kakaocdn.net/dn/ujBUB/hyZOEBKaOl/wGCNvwXF6P1Q30Q3dZOqn0/img.jpg?width=458&amp;amp;height=571&amp;amp;face=0_0_458_571,https://scrap.kakaocdn.net/dn/v1LRW/hyZOu0Zdvp/OZ4SFpNocgCwaJ3JK01Um1/img.png?width=335&amp;amp;height=335&amp;amp;face=0_0_335_335');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;테스트 주도 개발 TDD 실천법과 도구 | 채수원 - 교보문고&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;테스트 주도 개발 TDD 실천법과 도구 | 효율적인 설계와 간결한 코드를 만드는 필수 TDD 기법『TDD 실천법과 도구』는 업고픔질 소프트웨어를 만드는 유쾌한 개발 비법 TDD를 다룬 책이다. 초급 개&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;product.kyobobook.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;전통적인 개발 및 테스트의 문제점들&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;개발 기간이 길어질수록 개발자의 목표 의식이 흐려진다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;어디까지 짰더라?, 내가 뭘하고 있는거였지?, 이 모듈이 무슨 기능을 해야하더라?&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;분량이 늘어날수록 확인이 어려워진다.&lt;/li&gt;
&lt;li&gt;개발자의 집중력이 필요해진다&lt;/li&gt;
&lt;li&gt;코드의 사용방법과 변경 이력을 개발자의 기억력에 의존하게 돼서 논리적 오류를 찾기 어려워진다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;여기서 이값이 들어가고 이값이 나가는게 맞나??&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;테스트를 점차 간소화 하는 항목들이 늘어난다.&lt;/li&gt;
&lt;li&gt;코드 수정 시 기존 코드의 정상 동작에 대한 보장이 어렵다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;아 여기 고쳤는데 이거 다른 데서도 썼던가..?&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;테스트를 해보려면 기존 코드를 수정해야하는 등 번거로운 선행 작업이 필요할수있다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;입고 처리 테스트 하려면 우선 주문 완료됬다고 테이블을 업데이트 시켜야하고&amp;hellip;등등&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;테스트는 개발자의 노동력을 소모한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;TDD란 ?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메소드나 함수 같은 모듈을 작성할 떄 &amp;lsquo; 작성 종료 조건을 먼저 정해놓고코딩을 시작&amp;rsquo;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;빠른 피드백을 통해 만들어야할 프로그램&amp;rsquo;을 학습해나가는 점진적인 기법. 피드백과 학습&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;테스트 케이스를 작성하는 가장 기본적인 과정인 함수를 생성하기 전에 그 함수가 무엇 을 수행하고 무엇을 처리해야 하는지에 대해 생각해보는 것은 여행을 떠나기 전에 지도를 한번 살펴보는 것과 크게 다르지 않다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;내가 도착해야 할 곳이 어디인지를 마음속 에 선언하는 것은 내가 다른 유혹에 빠지지 않도록 해주고 본래의 의도 또한 좀 더 명확하 게 하는 데 큰 도움을 줄 수가 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;또한 이런 믿음과 확신을 바탕으로 테스트 케이스 를 동료와 공유함으로써 개발자의 인식 상태를 '나'의 코드가 얼마나 컴퓨터에서 잘 동작할 까 하고 고민하는 맹목적 효율성'으로부터 '우리'의 코드가 얼마나 쉽게 기억해낼 수 있고, 잘 이해할 수 있는지와 같은 '인간성'에 대한 실천으로 옮겨놓을 수 있다.&lt;/li&gt;
&lt;li&gt;간혹 개발자 로서 아키텍처 설계와 같은 구조적 혹은 기술적으로 아름다운 상위 레벨의 어떤 것들을 동경하게 된다.&lt;/li&gt;
&lt;li&gt;하지만 작은 피드백에 좀 더 신경을 쓰고 자신의 표현을 좀 더 인간의 어떤 것에 가깝게 하는 것은 작고 초라해 보일 수 있을지 몰라도 우리를 가장 안전하고 행복하 게 하는 책임감' 있는 움직임이라 이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;humans for humans(프로그래밍은 '사람'을 위해 '사람'이 행하는 인간적인 작업이다)&lt;/li&gt;
&lt;li&gt;TDD가 가지는 무엇인가에 '반응'하고 그것에 책임'을 수 정신과 관련된 내용.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;TDD에서 가장 중요한 사항은 무엇인가를 해결하는 데만 집중하는 것이 아니라, 하고자 하는 게 무엇인지에 정신이 머무르게 하는 것입니다.&lt;/li&gt;
&lt;li&gt;테스트를 통과 시키는 것은 나에게 주어진 '책임'에 정중히 '응답'하는 것.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;진행방식&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;질문 - 테스트작성을 통해 시스템에 질문한다.(결과는 실패)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;작성하고자 하는 메소드나 기능이 무엇인지 선별하고 작성 완료 조건을 정해서 실패하는 테스트케이스 작성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;응답- 테스트를 통과하는 코드를 작성해서 질문에 대답( 결과는 성공)&lt;/li&gt;
&lt;li&gt;리팩토링- 아이디어를 통합하고, 불필요한건 삭제하고, 모호한건 명환하게 리팩토링
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가독성이 적절한다&lt;/li&gt;
&lt;li&gt;중복된 코드는 없는가&lt;/li&gt;
&lt;li&gt;이름이나 잘못 부여된 메소드나 변수명은 없는가&lt;/li&gt;
&lt;li&gt;구조의 개선이 필요한 부분은 없는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;반복- 다음 질문을 통해 계속 진행&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;접근방식&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TDD에서는 테스트자동화를 통해서 개발이 시작된 시점부터 완료될 때 까지 가능한 빠른 시점내에, 자주 실패를경험하도록 유도한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OK 조건을 사전에 정해두고 빠르게 실패를 경험하며, 그 조건을 등대로 삼아 실패 상황을 최대한 빨리 극복하고자 노력한다.&lt;/li&gt;
&lt;li&gt;성공한 항목과 실패한 항목이 명확하고, 작업해야하는 부분이 확실하다.&lt;/li&gt;
&lt;li&gt;설공에 필요한 조건을 만들고, 실패하는 조건항목을 성공시킨다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;TDD를 하며 테스트케이스 자체가 &amp;ldquo;Amount 클래스의 구조에 대해 생각해봐라&amp;rdquo; 라는 말을 건다&lt;/li&gt;
&lt;li&gt;충분한 만큼의 테스트 케이스가 작성됐다 생각하면 제대로 구현시작
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모호하지만, 현재 집중하고 있는 요구 사항그 한가지만을 통과하기에는 적절한 숫자의 테스트케이스.&lt;/li&gt;
&lt;li&gt;경험이 축적됨에 따라 노련해지는 부분&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;TDD의 장점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;개발의 방향을 잃지 않게 유지해준다
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 자신이 어떤 기능을 개발하고 있고, 또 어디까지 와 있는지를 항상 살펴볼 수 있다.&lt;/li&gt;
&lt;li&gt;그리고 남은 단계와 목표를 잊지 않게 도와준다.&lt;/li&gt;
&lt;li&gt;TDD를 진행할 때 만들어지는 테스트 케이스들, 자신이 어디까지 왔고, 앞으로 나아가야 하는 곳이 어디인지를 알려주는 나침반이 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;품질 높은 소프트웨어 모듈 보유
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TDD를 통해 만들어진 애플리케이션은 필요한 만큼 테스트를 거친 품질이 검증된 부 품'을 갖게 되는 것과 마찬가지다.&lt;/li&gt;
&lt;li&gt;품질 좋은 부품이 꼭 품질 좋은 제품을 보장해주는 건 아니지만, 좋은 제품을 만드는 데 있어 기본 조건임에는 틀림없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;자동화된 단위 테스트 케이스를 갖게 된다
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TDD의 부산물로 나오는 자동화된 단위 테스트 케이스들은, 개발자가 필요한 시점에 언제든지 수행해볼 수 있다. 그리고 그 즉시 현재가자 작성된 시스템에 대한 이상 유무 를 바로 확인할 수 있다.&lt;/li&gt;
&lt;li&gt;또한 기능을 추가한다든가, 수정하게 됐을 때 수행해야 하는 회귀 테스트에 대한 부담도 줄이는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;사용설명서 &amp;amp; 의사소통의 수단
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TDD로 작성된 각 모듈에는 테스트 케이스라고 하는 테스트 코드가 개발 종료와 함께 남게 된다
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테스트 코드들의 가치는 시간이 지나면서 두고두고 빛을 발한다.&lt;/li&gt;
&lt;li&gt;그 가치 안에는 고객만을 위한 것이 아 니라, 현재의 자신과 주위의 개발자, 그리고 미래의 개발자에게 제공되는 상세화된 모 듈 사용 설명서라는 부분도 포함되어 있다.&lt;/li&gt;
&lt;li&gt;즐겨 보는 물건은 아니지만, 그렇다고 매뉴 얼 없는 TV 등의 가전 제품을 받는다면, 제품에 대한 느낌이 어떨까? TDD를 통해 작 성된 테스트 케이스는 사용 설명서이자, 그와 동시에 다른 개발자와 소통하는 커뮤니 케이션 통로가 된다.&lt;/li&gt;
&lt;li&gt;사실, 무한한 상상력을 필요로 하는 기존 코드(egacy codes)의 난해함과 문서화 부재 가 주는 상실감이 IT 프로젝트에서 얼마나 악영향을 미치는지에 대한 연구 사례는 많다.&lt;/li&gt;
&lt;li&gt;TDD는 이 부분에서도 장점을 갖는다. 일반적으로 TDD는 리팩토링과 단짝으로 진행되며, 리팩토링의 최대 미덕은 사람이 이해할 수 있는 코드로 만든다'이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;또한 TDD의 산물인 테스트 케이스들은 그 자체가 API들의 사용 예가 돼준다. 대부분의 개발자는 API 참조매뉴얼을 좋아하지 않는다. 그보다는 상황에 들어맞도록 잘 만들어진 예제를 더 좋아한다.&lt;/li&gt;
&lt;li&gt;테스트 케이스를 잘 작성하면, 개발 문서뿐 아니라 테스트 문서까 지도 함께 해결할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;설계 개선
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;테스트 케이스 작성 시에는 클래스나 인터페이스, 접근제어자(access modifier), 이름 짓기(naming), 인자(argument) 등에 이르는 개발에 포함된 다양한 설계 요소 들에 대해 미리부터 고민하게 된다.&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TDD에서 개발자는 자신이 작성한 프로그램에 대해 메소드 혹은 함수 단위로 테스트를 수행하며, 이는결과적으로 이후에 **발생한느 테스트 단계(통합, 인수..)에서의 결함 발생 비용을 줄여**준다.&lt;/li&gt;
&lt;li&gt;결함을 빨리 발견하면 할수록 적은 비용으로 처리가 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;흔히 테스트하기 어렵다고 생각되는 코드들은 객체 설계 원리 중 기본에 해당하는 원칙들이 잘못 적용됐거나 충분히 고려되지 않 았을 가능성이 높다.&lt;/li&gt;
&lt;li&gt;TDD를 진행해나가면서, 테스트가 가능하도록 설계 구조를 고 민하다 보면 자연스럽게 디자인을 개선하게 된다.&lt;/li&gt;
&lt;li&gt;즉 누가 모니터 옆에서 손가락으 로 개선해야 하는 부분을 가리켜주는 것이 아니라, 스스로의 사고와 수련으로 발전 해나갈 수 있게 도와준다.&lt;/li&gt;
&lt;li&gt;테스트 케이스를 작성할 때는, 작성하게 되는 모듈이 접할 상황에 대해 사전에 고 민해보고 준비하게 된다.&lt;/li&gt;
&lt;li&gt;미리 고민해보고 작성하는 것과 작성하면서 그때그때 추가하 는 것은, 모듈의 응집도와 의존성 정도에 있어 적지 않은 차이를 만들어낸다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;TDD가 주는 설계상의 이점
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TDD 자체가 단위 단위의 작은 설계를 만들어 낸다.&lt;/li&gt;
&lt;li&gt;입력과 출력, 해당 모듈이 동작하기 위해 필요한 요소 파악 등이 사전에 확실하게 고려되고 테스트 된다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이로 인해 다른 모듈에 대한 의존성이 상대적으로 적다.&lt;/li&gt;
&lt;li&gt;왜냐하면 다른 모듈에 의존성이 많아지면 적절한 테스트케이스를 작성하기가 점점 어려워 지기 때문에, 개발자가 스스로 모듈의 의존성을 줄이려는 노력을 초반부터 해나가게 된다.&lt;/li&gt;
&lt;li&gt;또한 각 모듈은 최대한 테스트를 독립적으로 수행할수 있도록 만들기 때문에 자기 오나결성이 높아진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;결과적으로 신뢰수준을 따져봐야 하는 모듈에 대해, 개발자가 접근해야 하는 영역을 좀 더 추상화된 상위 레벨로 올려줄수 있기 때문에, 소프트웨어의 복잡도가 낮아지고 개발자가 좀 더 일반화되고 추상된 형태의 디자인에 더 집중할 수 있게 도와 준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;TDD와 OOP
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TDD를 하면 기능과 객체의 관계를 스스로 먼저 고민하게 된다.&lt;/li&gt;
&lt;li&gt;그리고 떄떄로 현재와 같은 클래스 구조로는 테스트 코드를 미리 작성할 수가 없다는 사실을 스스로 알아차리게된다.&lt;/li&gt;
&lt;li&gt;테스트 케이스 코드는 입력과 출력이 명확하고, 사용되는 재료가 적으면 적을수록 작성하기 쉽다.&lt;/li&gt;
&lt;li&gt;즉, 의존과계가 많은 코드는 테스트 코드 자체를 만들기가 어렵다.&lt;/li&gt;
&lt;li&gt;사람의 본성이라는게 어려우면 안하게 된다.&lt;/li&gt;
&lt;li&gt;의존관계를 최대한 만들지 않고, 기능이 동작하도록 노력을 하게된다.&lt;/li&gt;
&lt;li&gt;TDD는 자신의 모듈에 필요한게 무엇인지를 미리미리 고민하게 만든다. 기능 위주로 테스트를 하기 때문에 불필요한 속성/필드가 무엇인지도 초반부터 밝혀진다.&lt;/li&gt;
&lt;li&gt;결과적으로 TDD는 좀 더 나은 설계, 좀 더 낭느 모듈 디자인이 될 수 있도록 스스로 생각할 수 있게 유도하고, 결과적으로는 모듈이 더 나은 구조를 가질 수 있게한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Mock&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모듈의 겉모양이 실제 모듈과 비슷하게 보이도록 만든 가짜 객체를 MOck이라고 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제 객체를 만들기엔 비용과 시간이 너무 많이 들거나 의존성이 길게 걸쳐져있어 제대로 구현하기 어려울 경우, 이런 가짜 객체를 만들어 사용한다.&lt;/li&gt;
&lt;li&gt;구현하는데 필요하지만 실제로 준비하기엔 여러가지 어려움이 따르는 대상을 필요한 부분만큼만 채워넣어서 만들어진 객체.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;언제 Mock 객체를 만드나?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모듈이 필요로하는 의존성은 테스트작성을 어렵게 만든다. 그리고 그 의존성을 단절시키기 위해 사용된다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;포비의 팀 내에 테스트 개발 문화 만들기&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;팀장의 역할
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단위 테스트의 필요성을 인지하고 적극적으로 지원해줘야 한다.&lt;/li&gt;
&lt;li&gt;프로젝트 초반에 개발 생산성이 저하될 가능성이 크기 때문에 효과가 나타날 때까지 인내하고 기다려줄 수 있어야 한다.&lt;/li&gt;
&lt;li&gt;단위 테스트 및 소스코드 품질의 중요성을 강조하고, 잘하는 개발자에게 포상을 해줘야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;팀원의 역할
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단위 테스트의 필요성을 인지하고 단위 테스트를 만드는 데 적극적으로 참여해야 한다. 필요 없다고 판단된다면 적극적으로 의견 개진한다.&lt;/li&gt;
&lt;li&gt;모든 개발자가 단위 테스트를 작성해야 한다. 일부 개발자만 참여할 경우 효과가 희석되거나 실패할 가능성이 많다.&lt;/li&gt;
&lt;li&gt;좋은 단위 테스트 코드를 만들기 위해 끊임없이 공부하고 노력할 자세가 되어 있어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;팀의 역할
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단위 테스트의 필요성을 인지하고 팀의 문화로 만들기 위한 지속적인 노력을 한다.&lt;/li&gt;
&lt;li&gt;팀 구성원끼리 지속적으로 정보를 공유하고 배우려는 문화를 정착시켜야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;하지 말아야 할 일
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단위 테스트를 내가 원해서가 아니라 위에서 시키니까 한다는 수동적인 자세로 시작할 계획이 라면 하지 않는 편이 낫다. 오히려 시간 낭비가 될 가능성이 많다.&lt;/li&gt;
&lt;li&gt;항상 100%가 실행되지 않는 테스트 코드는 추후 쓰레기가 될 가능성이 높다. 쓰레기가 된 테 스트 코드를 유지보수하는 비용은 낭비되는 시간일 뿐이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;단위 테스트의 효과를 높일 수 있는 팁
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;지속적 통합 툴(C)을 도입하여 자동화된 단위 테스트가 가능하도록 한다.&lt;/li&gt;
&lt;li&gt;Test Coverage 리포트에서 복잡도가 높으나 단위 테스트 Coverage가 낮은 코드를 우선적으 로 단위 테스트한다.&lt;/li&gt;
&lt;li&gt;가능하면 테스트 코드를 먼저 만드는 TDD를 습관화한다. 단, 많은 시간과 노력이 필요하다.&lt;/li&gt;
&lt;li&gt;코드의 복잡도를 낮출 수 있도록 지속적으로 리팩토링한다.&lt;/li&gt;
&lt;li&gt;테스트하기 쉬운 코드를 만들기 위해 노력한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;유의사항&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테스트케이스는 이름이 중요하다
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이름이 길어지더라도 해당 테스트가 무엇이고, 실패한 세부 항목이 무엇인지 곧바로 알수 있어야한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;더이상 제대로 동작하지 않는 테스트케이스는 제거한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자동화된 테스트는 소스의 품질을 좌우하는 중요한 자산이지만, 많다고 무조건 좋진않다.&lt;/li&gt;
&lt;li&gt;중복된 케이스나 업무 변경등의 이유로 제대로 동작하지 않는 테스트 케이스는 제거한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;TDD는 자동화된 테스트를 만드는것이 목표가 아니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TDD는 개발의 목표 지점을 미리 정하기 위해 단위 테스트 케이스를 만들고, 목표 상태 도달 여부를 빨리 확인하기 위해 단위 테스트 케이스를 자동화 시킨다.&lt;/li&gt;
&lt;li&gt;자동화된 단위 테스트들은 TDD의 부산물이다.&lt;/li&gt;
&lt;li&gt;개발과 설계를 위한 보조 도구이지, 목적은 아니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;하나의 테스트케이스는 하나만 테스트 하도록 작성한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러개의 실패하는 테스트케이스를 한번에 만들지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;테스트케이스는 최대한 고립시킨다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각각의 단위 테스트는 다른 모듈이나 시스템에 최대한 독립적이고 고립된 형태로 작성될수록 단단한 테스트가 될 수 있다.&lt;/li&gt;
&lt;li&gt;그래서 가급적이면 아래와 같은건 들어가지 않도록 작성해야한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테스트케이스가 작성되어있지 않은 모듈&lt;/li&gt;
&lt;li&gt;DB,외부 시스템, 콘솔출력, 네트웤,&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;물론 위 나열한것 자체를 테스트한다면 달라지겠지만, 대부분의 경우 현재 기능을 위해 외부에 의존하는 것들이다.&lt;/li&gt;
&lt;li&gt;결국 최대한 의존관계를 줄이고, 경우에 따라Mock객체를 이용해서라도 독립성을 보장해줘야 특정 테스트 실패에 따른 실패 전파현상을 최소한으로 줄일 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;생성자테스트
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체 사용을 위해 반드시 갖춰야 하는 값을 생성자에 설정하는 경우 필요&lt;/li&gt;
&lt;li&gt;선행조건이나 업무 로직이 섞여있는경우&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;테스트코드에서는 중복에 대한 접근이 약간다르다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;중복 되는 부분이 테스트 시나리오의 일부라면 곰니해봐야한다.&lt;/li&gt;
&lt;li&gt;테스트 메소드가 하나의 문맥 측면에서 완결성을 갖는것이 중요할떄도 있음.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;텍스트 픽스처 메서드는 그럼 언제?&lt;/li&gt;
&lt;li&gt;테스트 시나리오에 참가하고 있는 객체들에게 테스트에 필요한 사전 조건이나 기반 환경을 제공할때.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;유연한 코드
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;변경이 쉬워지려면 코드가 유연해야하는데, 유연함이란 &amp;lsquo;개발비용증가&amp;rsquo;의 또 다른 요소가 된다.&lt;/li&gt;
&lt;li&gt;&amp;lsquo;현재 필요한 기능에만 최대한 집줍&amp;rsquo;하는게 적절한 선택이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;자동화된 테스트케이스는 소스가 작성된 후 작성하려고 하면, 심리적인 요인에 다가 테스트 작성에 소요되는 시간과 육체적 상황으로 인해 쉽지 않다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아 귀찮아 이미 잘 돌고 있다구 !&lt;/li&gt;
&lt;li&gt;어우 피곤해 그만하고 쉬어야지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;TDD를 할때 하나의 단계가 끝날 때 마다 리팩토링을 하면, 대상 코드 뿐 아니라 자동화된 테스트케이스 자체도 사용하기 좋은 코드로 정련된다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리팩토링은 복잡하거나 어려운 그 무엇, 이를때면 디자인 패턴 같은 부류가 절대 아니다.&lt;/li&gt;
&lt;li&gt;쉽게 배우고 크게 효과를 볼 수 있어야한다.&lt;/li&gt;
&lt;li&gt;궁극적인 목표는 &amp;lsquo;이해하기 쉽고, 수정하기 쉬운 코드로 만들자&amp;rsquo;이다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;TDD는 설계를 위한 기법이고, 작업을 해나가려면 떄떄로 많은 사고와 전략이 필요한 기법이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;보통사용자 스토리 보드를 완성하기 위한 세부 TOdo 리스트가 만들어진 다음에, 짝 프로그래밍으로 TDD를 적용해보면 효과가 더 높다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;TDD로 개발하려면 단순히 자판은 누르면서 진행해나가는 시행착오 반복 식 보다 초반에 고민해야하는 요소가 더 많다.&amp;rarr; 큰 에너지 소비이자 고통,&lt;/li&gt;
&lt;li&gt;TDD를 어렵게 하는 요인
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TDD에 집중하는건 단위 기능(메소드)이지만 객체지향언어로 작성된 대부분은 클래스들로 구성된다.&lt;/li&gt;
&lt;li&gt;클래스는 API나 라이버르리와 달리 권한, 위임, 책임 등을 갖고 외부와 통신한다.&lt;/li&gt;
&lt;li&gt;TDD라는 행위 자체, 혹은 TDD를 통해 만들어진 자동화 된 단위 테스트는 그 영역을 커버하지 못한다. 그래서 전체적으로 산만한 아키텍쳐가 되거나 한쪾으로 치우칠수있따.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이런 현상은 TDD만이 지나치게 강조됐을 떄 발생한다.&lt;/li&gt;
&lt;li&gt;해결방법은 TDD를 진행하기 앞서 적절한 레벨의 설계를 선행하는것이다.&lt;/li&gt;
&lt;li&gt;적어도 고수준 설계 정도는 마친다음 TDD를 적용해야 이런문제를 막을수있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;우리는 종종 테스트 코드를 작성해야한다에 지나치게 집중해버린 나머지 TDD의 목표가 테스트 작성이 아닌 좀 더 나은 설계에 있다는 사실을 잊느다.&lt;/li&gt;
&lt;li&gt;TDD진행시, 억지로 테스트를 작성하는 건 잘못된 접근이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테스트를 작성하기 쉽게코드를 작성하는게 맞는거다.&lt;/li&gt;
&lt;li&gt;테스트를 만들기 어렵다면, 기본적으로 코드 설계를 다시 봐야한다.&lt;/li&gt;
&lt;li&gt;어떻게든 테스트케이스를 작성하기 위해 다양한 트릭(Mcok 프레임워크 대거 사용), 테스트 유틸리티 작성 &amp;rarr; 결국 유지하기 어렵다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;TDD는 버그를 없애기 위한 방법론이 아니다.&lt;/li&gt;
&lt;li&gt;해당 프로그램의 특성, 도메인의 성격에 맞는, 그리고 작업하는 팀에 맞는 테스트 커버리지 비율을 찾아라.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자동화된 테스트 케이스는 TDD의 부산물이자, 리팩토링을 위한 안전장치에 더 가깝다.&lt;/li&gt;
&lt;li&gt;그 자체를 순수 목적으로 삼게되면, 더이상 TDD의 영역이 아니게 된다.&lt;/li&gt;
&lt;li&gt;그건 테스트의 영역이 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>외부활동/우아한테크코스 [프리코스]</category>
      <author>softmoca__</author>
      <guid isPermaLink="true">https://softmoca.tistory.com/554</guid>
      <comments>https://softmoca.tistory.com/554#entry554comment</comments>
      <pubDate>Thu, 13 Nov 2025 16:27:02 +0900</pubDate>
    </item>
    <item>
      <title>[우아한테크코스 8기] 프리코스 오픈미션 2차 회고- 자동차 경주 TDD</title>
      <link>https://softmoca.tistory.com/553</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAmAjF/dJMcaiuXluz/eKanwFJW9VYGYDAXLKKKV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAmAjF/dJMcaiuXluz/eKanwFJW9VYGYDAXLKKKV0/img.png&quot; data-origin-width=&quot;732&quot; data-origin-height=&quot;497&quot; data-is-animation=&quot;false&quot; style=&quot;width: 51.9396%; margin-right: 10px;&quot; data-widthpercent=&quot;52.55&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAmAjF/dJMcaiuXluz/eKanwFJW9VYGYDAXLKKKV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAmAjF%2FdJMcaiuXluz%2FeKanwFJW9VYGYDAXLKKKV0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;732&quot; height=&quot;497&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/D6uHX/dJMcadtEbKZ/uabxXaJG5f0fvfQSkBkBw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/D6uHX/dJMcadtEbKZ/uabxXaJG5f0fvfQSkBkBw0/img.png&quot; data-origin-width=&quot;766&quot; data-origin-height=&quot;576&quot; data-is-animation=&quot;false&quot; style=&quot;width: 46.8976%;&quot; data-widthpercent=&quot;47.45&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/D6uHX/dJMcadtEbKZ/uabxXaJG5f0fvfQSkBkBw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FD6uHX%2FdJMcadtEbKZ%2FuabxXaJG5f0fvfQSkBkBw0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;766&quot; height=&quot;576&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/M1FG1/dJMcadAo90f/VzegQKob1AmTYggNA6NPmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/M1FG1/dJMcadAo90f/VzegQKob1AmTYggNA6NPmk/img.png&quot; data-origin-width=&quot;756&quot; data-origin-height=&quot;638&quot; data-is-animation=&quot;false&quot; style=&quot;width: 48.0248%; margin-right: 10px;&quot; data-widthpercent=&quot;48.59&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/M1FG1/dJMcadAo90f/VzegQKob1AmTYggNA6NPmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FM1FG1%2FdJMcadAo90f%2FVzegQKob1AmTYggNA6NPmk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;756&quot; height=&quot;638&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dtwZNo/dJMcahbMObP/DzHHqRWX7p0Bj2IbXhbW7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dtwZNo/dJMcahbMObP/DzHHqRWX7p0Bj2IbXhbW7k/img.png&quot; data-origin-width=&quot;756&quot; data-origin-height=&quot;603&quot; data-is-animation=&quot;false&quot; style=&quot;width: 50.8124%;&quot; data-widthpercent=&quot;51.41&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dtwZNo/dJMcahbMObP/DzHHqRWX7p0Bj2IbXhbW7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdtwZNo%2FdJMcahbMObP%2FDzHHqRWX7p0Bj2IbXhbW7k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;756&quot; height=&quot;603&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/softmoca/java-racingcar-8/tree/softmoca-tdd&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/softmoca/java-racingcar-8/tree/softmoca-tdd&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1764212996595&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - softmoca/java-racingcar-8&quot; data-og-description=&quot;Contribute to softmoca/java-racingcar-8 development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/softmoca/java-racingcar-8/tree/softmoca-tdd&quot; data-og-url=&quot;https://github.com/softmoca/java-racingcar-8&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ez8Ds0/hyZOu0X9fw/sByYGAokBxaecxflewPQxK/img.png?width=1200&amp;amp;height=600&amp;amp;face=988_120_1036_172,https://scrap.kakaocdn.net/dn/bm4B1f/hyZOol6EwX/CQmJdkkFgjBaHl1nRR4P71/img.png?width=1200&amp;amp;height=600&amp;amp;face=988_120_1036_172&quot;&gt;&lt;a href=&quot;https://github.com/softmoca/java-racingcar-8/tree/softmoca-tdd&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/softmoca/java-racingcar-8/tree/softmoca-tdd&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ez8Ds0/hyZOu0X9fw/sByYGAokBxaecxflewPQxK/img.png?width=1200&amp;amp;height=600&amp;amp;face=988_120_1036_172,https://scrap.kakaocdn.net/dn/bm4B1f/hyZOol6EwX/CQmJdkkFgjBaHl1nRR4P71/img.png?width=1200&amp;amp;height=600&amp;amp;face=988_120_1036_172');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - softmoca/java-racingcar-8&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to softmoca/java-racingcar-8 development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;시작하며&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2번째 TDD 경험을 마쳤다 ! 1번째에서도 예상보다 시간과 리소스가 많이 소요 되었지만 이번에는 훨씬 더 예상 보다 많은 시간이 소요되어 결국 후반부터 의식적으로 프로젝트를 마무리 짓기 위해 노력했다. 가장 큰 이유는 2가지이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째로 컨트롤러와 입출력 테스트가 생각보다 너무 많은 시간과 리소스를 잡아 먹었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음으로 Mock을 사용해 런던파 스타일로 테스트를 작성해 보았는데 상상 이상으로 힘들었다..!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두번째는 기본적인 기능 구현 후 습관적으로 개선과 리팩토링할 사항을 OOP적으로 깊게 생각하며 몰입의 길을 잃어 다시금 인식하고 길을 찾는데 많은 시간이 소요 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 TDD가 처음 개발과 입문 시 오랜 시간이 걸리다는걸 인지해 계획 일정을 세워서 나름 다행이라는 생각을 했다 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&amp;ldquo;요구 사항 탐색 &amp;amp; 설계 피드백 도구로서의 TDD&amp;rdquo;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그간 그저 기계적으로 랜덤한 값과 관련된 테스트는 인터페이스를 통해 설계를 했었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 TDD로 테스트를 먼저 작성하고 이후 작업을 고민하는 과정을 통해 명확한 순서로 필요성을 인지하고 설계를 정하는 경험을 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 과정에서 다시 한번 테스트를 단순한 검증 도구가 아닌, 요구사항 탐색도구이자 설계 피드백 도구로서 인지했다 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 자동차가 전진하면 위치가 증가하는지 확인하고 싶다는 생각으로 car.move() 후 위치를 검증하는 테스트를 작성했지만, 테스트를 작성하는 순간 무작위 값 때문에 항상 일정한 결과가 나오지 않는다는 문제를 발견했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 테스트가 실패한 것이 아니라, 요구사항과 설계 구조 자체가 테스트와 맞지 않는 다는걸 발견 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 후 전진 여부를 내부 랜덤 로직이 아닌 외부에서 주입받도록 설계를 변경했고, 이후에는 단순한 boolean이 아닌 MovingStrategy라는 인터페이스로 전진 조건을 분리하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 Car 객체가 전진 여부를 판단하는 것이 아니라, 전진하라는 명령을 받으면 전진만 수행하는 역할을 가져야 한다는 책임 분리의 중요성 느꼈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로 랜덤이라는 테스트 불가능 요소를 제거하는 과정에서 의존성 주입, 책임 분리, 전략 패턴과 같은 설계 개념들을 코드로 자연스럽게 녹여낼 수 있었고, 이 과정 자체가 요구사항을 더 정확히 재해석하고 구조를 더 견고하게 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그저 이론적으로 암기식으로 알고 있던 느낌을 실제로 TDD를 통해 나의 손과 눈과 머리로 이런 과정을 느낄수 있어 너무 짜릿했다 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&amp;ldquo;리팩토링 안전망으로서의 TDD&amp;rdquo;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기 컨트롤러는 입력, 도메인 생성, 게임 실행, 출력을 모두 한 번에 처리하는 구조였고 테스트가 어려운 상태였다. TDD를 통해 살아있는 테스트 코드가 있는 상태에서 입출력을 인터페이스로 분리하며 의존성 주입 기반으로 구조를 과감하게 수정할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 static 구조에서 인스턴스 구조로의 대규모 변경에도 불구하고 모든 테스트가 통과하는 것을 보며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TDD가 구조 변경의 족쇄가 아니라, 리팩토링을 가능하게 하는 안전망이라는 사실을 또한번 체감했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에는 &amp;ldquo;고치면 깨질까 봐&amp;rdquo; 리팩토링을 회피했지만, 이번에는 &amp;ldquo;일단 고쳐보고 테스트로 확인하자&amp;rdquo;라는 태도로 설계를 개선할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생각보다 많은 시간과 리소스가 소요 되었지만 막막함과 혼란스러움 보다 온전히 오랜 시간이 걸려서 힘들다 라는 점 말고는 리팩토링 안전망으로서의 기능을 온전히 느꼈다 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 역시 TDD의 심리적, 기술적 안정 장치를 통한 효과라고 느껴져서 너무 뿌듯했다 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&amp;ldquo;컨트롤러 테스트의 비효율&amp;rdquo;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨트롤러 테스트를 작성하는 과정에서 &amp;ldquo;모든 테스트가 같은 가치를 가지지는 않는다&amp;rdquo;는 사실을 몸소 온전히 느낀 경험을 했다. 입출력을 테스트하기 위해 여러 인터페이스를 도입하고, FixedInputReader, MockOutputWriter 같은 테스트용 객체를 구현하며 구조를 크게 개선했지만, 그만큼 테스트 작성과 유지 비용도 급격히 증가했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 MockOutputWriter를 관리하는 과정에서, 실제 검증 로직보다 Mock 세팅 코드가 더 길어지고 복잡해지는 상황을 겪으며 큰 의문이 들었다.&amp;ldquo;이 테스트는 정말 필요한가..?&amp;rdquo;, &amp;ldquo;아니면 테스트를 위해 테스트를 만들고 있는 건 아닐까?&amp;rdquo;라는 고민이 자연스럽게 따라왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨트롤러 테스트는 결국 입출력 메서드가 어떤 순서로 몇 번 호출되었는지와 같은 흐름을 검증하는 수준에 머물렀고, 핵심 비즈니스 로직은 이미 도메인 테스트에서 충분히 보장되고 있었다. 하지만 컨트롤러 테스트는 도메인 테스트보다 훨씬 많은 리소스를 요구했고, 리팩토링 시 오히려 발목을 잡을 정도로 구현 세부사항에 강하게 결합되고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 Mock 기반 테스트는 메서드 이름 변경, 호출 순서 변경 같은 리팩토링 과정에서 쉽게 깨지며, 테스트 자체의 유지보수 비용을 급격히 증가시켰다. 이 시점에서 테스트가 설계를 돕는 도구가 아니라, 설계를 억제하는 족쇄처럼 느껴지기 시작했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 나는 이후에는 컨트롤러에 대해 모든 것을 자동화 테스트로 검증하려는 욕심을 내려놓고, 비즈니스 로직이 있는 도메인 테스트에 집중하고, 컨트롤러는 통합 테스트와 수동 테스트로 보완하기로 결론을 지었다 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트도 하나의 코드이기에, 유지 비용 대비 얻는 가치를 고려해야 했고, 지금의 컨트롤러 테스트는 그 균형이 전혀 맞지 않는다고 판단했기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 여러 프로젝트 상황에 따라 다르겠지만 일반적으로 접하게 되는 여러 상황에서는 컨트롤러 테스트는 분명 독으로 작용할것 같다 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이런 경험들 격었기에 &amp;ldquo;테스트를 많이 쓰는 것&amp;rdquo;이 중요한 것이 아니라, &amp;ldquo;가치 있는 테스트에 집중하는 것&amp;rdquo;이 훨씬 중요하다는 이론적인 내용을 온몸으로 느낄 수 있었다 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매우 힘들었지만 오픈미션의 목표였던 &amp;ldquo;직접 경험하고 체득&amp;rdquo;을 잘 하고 있어 뿌듯했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&amp;rdquo;OOP 학습과 TDD 학습의 모순&amp;rdquo;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능 구현을 모두 마친 후 여러 개선과 리팩토링 사항을 고민하는 과정에서 OOP와 TDD 사이의 모순과 충돌을 느꼈다. 첫 시작과 목적은 지금 필요한 것만 만들고, 과도한 설계를 피하기 위한 TDD의 원칙이었지만, 어느 순간 &amp;ldquo;유지보수성을 위해 구조를 미리 잘 설계해야 한다&amp;rdquo;는 OOP 학습을 하려고 끙끙대는 날 발견했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 이 두 가지를 동시에 완벽하게 만족시켜보기 위해 책임을 더 잘 나누고, 역할을 더 분리하고, 확장 가능한 구조를 만들고, Value Object를 최대한 도입하려고 노력했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 그 과정에서 불필요하게 코드 복잡도가 증가하며, &amp;lsquo;개발 방향성을 놓치지 않게 해준다&amp;rsquo;는 TDD의 핵심 장점을 완전히 놓으며 잘못된 몰입을 하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연히 TDD와 OOP는 같은 방향을 바라볼것 이라 생각했지만 실제로는 전혀 추구하는 가치가 달랐던 걸 깨달았다 ! TDD는 &amp;ldquo;지금 요구사항을 정확하게 해결하는 것&amp;rdquo;에 집중하고, OOP는 &amp;ldquo;미래 변경에 잘 대응할 수 있는 구조를 만드는 것&amp;rdquo;에 집중한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 후 오픈 미션을 진행하는 동안 나만의 기준을 세웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설계는 목적이 아니라 수단이며, 리팩토링은 &amp;lsquo;해야 해서&amp;rsquo;가 아니라 &amp;lsquo;필요해서&amp;rsquo; 하는 것이라는 기준 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이후에는 이론적으로 좋아 보이는 설계를 미리 적용하기보다 실제 사용 과정에서 불편함과 문제를 직접적으로 느낄 때 리팩토링과 구조 개선을 하려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜냐하면 이번 나의 목표는 TDD이기 때문이다 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경험을 통해 완벽한 설계란 존재하지 않으며, 모든 설계는 항상 상황과 맥락에 따른 트레이드오프의 결과라는 것을 체감하게 또 다시 느꼈다. 중요한 것은 이론적으로 멋진 구조가 아니라 현재 문제를 가장 잘 해결하면서도, 나중에 고칠 수 있는 여지를 남기는 균형이라는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 현재의 나는 이 또한 꾸준한 학습과 경험이 동반되어 OOP적으로나 TDD적으로나 균형을 가지며 학습하고 성장해야할것 같다고 생각한다. 지금이야 TDD를 입문하는 입문자로서 이런 기준을 세웠지만 실제 실무에서는 TDD사이클 사이사이 제때 필요한 OOP적인 사고를 잘 녹여내야 한다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;160&quot; data-start=&quot;154&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;마치며&lt;/b&gt;&lt;/h2&gt;
&lt;p data-end=&quot;239&quot; data-start=&quot;162&quot; data-ke-size=&quot;size16&quot;&gt;1차 TDD 경험이 &amp;ldquo;충격과 깨달음의 시작&amp;rdquo;이었다면,이번 2차 경험은 그 충격 이후 마주한 현실적인 혼란과 선택의 연속이었다.&lt;/p&gt;
&lt;p data-end=&quot;360&quot; data-start=&quot;241&quot; data-ke-size=&quot;size16&quot;&gt;단순히 TDD를 한다는 것만으로는 충분하지 않았다.&lt;br /&gt;어디에 힘을 써야 하는지, 어디까지 테스트를 해야 하는지, 어디서 멈춰야 하는지에 대해 모든 것을 스스로 판단해야 하는 순간들이 계속해서 찾아왔다.&lt;/p&gt;
&lt;p data-end=&quot;469&quot; data-start=&quot;362&quot; data-ke-size=&quot;size16&quot;&gt;컨트롤러 테스트에서 느낀 비효율, Mock 기반 런던파 스타일의 난이도, OOP 학습과 TDD 실천 사이에서의 충돌은 이제 이론이 아니라 직접 겪은 현실적인 딜레마가 되었다.&lt;/p&gt;
&lt;p data-end=&quot;570&quot; data-start=&quot;471&quot; data-ke-size=&quot;size16&quot;&gt;그럼에도 이번 경험이 의미 있었던 이유는, &amp;lsquo;완벽하게 해냈기 때문&amp;rsquo;이 아니라 헷갈려보고, 과하게 해보고, 실패해보고, 그 속에서 기준을 세웠기 때문이라고 생각한다.&lt;/p&gt;
&lt;p data-end=&quot;649&quot; data-start=&quot;572&quot; data-ke-size=&quot;size16&quot;&gt;특히 이번 미션을 통해 얻은 가장 큰 것은 TDD를 기법이 아니라, 상황에 맞게 조율해야 하는 도구로 바라보게 되었다는 점이다.&lt;/p&gt;
&lt;p data-end=&quot;815&quot; data-start=&quot;731&quot; data-ke-size=&quot;size16&quot;&gt;중요한 것은 지금 내가 무엇을 배우고 있는지, 어디에 시간을 써야 하는지, 어디서 멈출 수 있는지를 스스로 의식하며 선택하는 과정이었다.&lt;/p&gt;
&lt;p data-end=&quot;903&quot; data-start=&quot;817&quot; data-ke-size=&quot;size16&quot;&gt;또한 이번 2차 경험을 통해 OOP와 TDD가 충돌하는 것이 아니라 서로 다른 시간 축에 존재하는 개념이라는 사실을 조금은 이해하게 되었다.&lt;/p&gt;
&lt;p data-end=&quot;937&quot; data-start=&quot;905&quot; data-ke-size=&quot;size16&quot;&gt;TDD는 지금을 다루고, OOP는 내일을 준비하는 것 !!&lt;/p&gt;
&lt;p data-end=&quot;994&quot; data-start=&quot;939&quot; data-ke-size=&quot;size16&quot;&gt;나는 이제 그 사이에서 상황에 맞게 중심을 옮겨 가며 선택하는 연습을 하고 있다는 생각이 든다.&lt;/p&gt;
&lt;p data-end=&quot;1048&quot; data-start=&quot;996&quot; data-ke-size=&quot;size16&quot;&gt;아직은 많이 서툴고, 여전히 헷갈리는 순간도 많지만, 분명한 건 나는 이제 TDD를 &amp;ldquo;따라 해보는 입문자&amp;rdquo;가 아니라 TDD 속에서 나만의 기준을 만들어가는 학습자가 되어가고 있다는 것이다.&lt;/p&gt;
&lt;p data-end=&quot;1196&quot; data-start=&quot;1116&quot; data-ke-size=&quot;size16&quot;&gt;이제 중요한 건 또 한 번의 미션, 또 한 번의 실패, 또 한 번의 선택 속에서 이 기준들을 조금씩 더 다듬어 가는 일일 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;꽤나 많이 지치고 몇번의 좌절과 무기력을 느끼기도 했지만 이러한 모든&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp;과정 자체가,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;내가 이번 오픈 미션을 통해 가장 얻고 싶었던&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;진짜 경험이라고 생각한다 !!&lt;/p&gt;</description>
      <category>외부활동/우아한테크코스 [프리코스]</category>
      <author>softmoca__</author>
      <guid isPermaLink="true">https://softmoca.tistory.com/553</guid>
      <comments>https://softmoca.tistory.com/553#entry553comment</comments>
      <pubDate>Thu, 13 Nov 2025 12:48:50 +0900</pubDate>
    </item>
    <item>
      <title>[우아한테크코스 8기] 프리코스 오픈미션 1차 회고- 문자열계산기 TDD</title>
      <link>https://softmoca.tistory.com/549</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dXa2JX/dJMcabbzsCD/m35Y4Kq4LZO5AycNys8Ff1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dXa2JX/dJMcabbzsCD/m35Y4Kq4LZO5AycNys8Ff1/img.png&quot; data-origin-width=&quot;620&quot; data-origin-height=&quot;531&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.2185%; margin-right: 10px;&quot; data-widthpercent=&quot;32.99&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dXa2JX/dJMcabbzsCD/m35Y4Kq4LZO5AycNys8Ff1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdXa2JX%2FdJMcabbzsCD%2Fm35Y4Kq4LZO5AycNys8Ff1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;620&quot; height=&quot;531&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c6czzF/dJMcaiBIAEf/h24UqkbyWMi3I1jZMre4i1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c6czzF/dJMcaiBIAEf/h24UqkbyWMi3I1jZMre4i1/img.png&quot; data-origin-width=&quot;718&quot; data-origin-height=&quot;563&quot; data-is-animation=&quot;false&quot; style=&quot;width: 35.1904%; margin-right: 10px;&quot; data-widthpercent=&quot;36.03&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c6czzF/dJMcaiBIAEf/h24UqkbyWMi3I1jZMre4i1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc6czzF%2FdJMcaiBIAEf%2Fh24UqkbyWMi3I1jZMre4i1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;718&quot; height=&quot;563&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cUTGNR/dJMcacO45XR/6MPS3KkaSqEai3a8AfJ9Ak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cUTGNR/dJMcacO45XR/6MPS3KkaSqEai3a8AfJ9Ak/img.png&quot; data-origin-width=&quot;623&quot; data-origin-height=&quot;568&quot; data-is-animation=&quot;false&quot; style=&quot;width: 30.2655%;&quot; data-widthpercent=&quot;30.98&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cUTGNR/dJMcacO45XR/6MPS3KkaSqEai3a8AfJ9Ak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcUTGNR%2FdJMcacO45XR%2F6MPS3KkaSqEai3a8AfJ9Ak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;623&quot; height=&quot;568&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d0MGXA/dJMcajgjX5D/ZiFv1Tb6c9xk2wxkeiE7C1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d0MGXA/dJMcajgjX5D/ZiFv1Tb6c9xk2wxkeiE7C1/img.png&quot; data-origin-width=&quot;708&quot; data-origin-height=&quot;522&quot; data-is-animation=&quot;false&quot; style=&quot;width: 34.0962%; margin-right: 10px;&quot; data-widthpercent=&quot;34.91&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d0MGXA/dJMcajgjX5D/ZiFv1Tb6c9xk2wxkeiE7C1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd0MGXA%2FdJMcajgjX5D%2FZiFv1Tb6c9xk2wxkeiE7C1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;708&quot; height=&quot;522&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bM689X/dJMcagYfwo9/N8NXlCjFE7o1FGd7wGezP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bM689X/dJMcagYfwo9/N8NXlCjFE7o1FGd7wGezP1/img.png&quot; data-origin-width=&quot;794&quot; data-origin-height=&quot;605&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.992%; margin-right: 10px;&quot; data-widthpercent=&quot;33.78&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bM689X/dJMcagYfwo9/N8NXlCjFE7o1FGd7wGezP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbM689X%2FdJMcagYfwo9%2FN8NXlCjFE7o1FGd7wGezP1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;794&quot; height=&quot;605&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHM8Nd/dJMcaaRgVMr/KwJx5dk2hRju1QOdf5yatK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHM8Nd/dJMcaaRgVMr/KwJx5dk2hRju1QOdf5yatK/img.png&quot; data-origin-width=&quot;685&quot; data-origin-height=&quot;563&quot; data-is-animation=&quot;false&quot; style=&quot;width: 30.5862%;&quot; data-widthpercent=&quot;31.31&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHM8Nd/dJMcaaRgVMr/KwJx5dk2hRju1QOdf5yatK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHM8Nd%2FdJMcaaRgVMr%2FKwJx5dk2hRju1QOdf5yatK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;685&quot; height=&quot;563&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/softmoca/java-calculator-8/tree/softmoca-tdd&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/softmoca/java-calculator-8/tree/softmoca-tdd&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1764212528208&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - softmoca/java-calculator-8&quot; data-og-description=&quot;Contribute to softmoca/java-calculator-8 development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/softmoca/java-calculator-8/tree/softmoca-tdd&quot; data-og-url=&quot;https://github.com/softmoca/java-calculator-8&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bEbSLW/hyZOn1QpcK/niTkJ4dZTYG5Q8Az1D2gq1/img.png?width=1200&amp;amp;height=600&amp;amp;face=988_120_1036_172,https://scrap.kakaocdn.net/dn/KG46P/hyZNCZRZ1a/n1OLKK8tCocpNaFfoKK88K/img.png?width=1200&amp;amp;height=600&amp;amp;face=988_120_1036_172&quot;&gt;&lt;a href=&quot;https://github.com/softmoca/java-calculator-8/tree/softmoca-tdd&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/softmoca/java-calculator-8/tree/softmoca-tdd&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bEbSLW/hyZOn1QpcK/niTkJ4dZTYG5Q8Az1D2gq1/img.png?width=1200&amp;amp;height=600&amp;amp;face=988_120_1036_172,https://scrap.kakaocdn.net/dn/KG46P/hyZNCZRZ1a/n1OLKK8tCocpNaFfoKK88K/img.png?width=1200&amp;amp;height=600&amp;amp;face=988_120_1036_172');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - softmoca/java-calculator-8&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to softmoca/java-calculator-8 development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;시작하며&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드디어 첫 TDD 경험을 마쳤다 ! 처음 예상했던 것 보다 훨씬 시간과 리소스가 많이 소요 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 시도를 하려고하니 여러 레퍼런스와 도서에 읽은 너무 많은 방법들이 떠오르며 중간중간 각 레퍼런스마다 모순되는 내용들이 떠올라 상당히 혼란스러웠다. TDD에서의 요구 사항 분석 및 문서화, 리드미 작성, 커밋 방법, 프리코스 미션의 요구 사항들 등등 모두 고려 하다 보니 실직적으로 내가 현재 해당 프로젝트를 왜 진행하는지 방향성을 잃기도 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 여러번 프로젝트를 뒤엎던 중 다시금 목표를 명확히 세워 정리하고, 세부적인 모든 규칙과 기록을 제외 하고 온전히 &amp;lsquo; TDD를 체득하며 격는 여러 경험&amp;rsquo;에 몰입하기로 방향을 정했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&amp;ldquo; 요구사항 탐색 도구로서의 TDD&amp;rdquo;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단위 테스트는 모두 통과했고, 커스텀 구분자 파싱 로직도 완벽하게 작동했다. 하지만 ApplicationTest를 실행하니 실패했다. 해당 테스트를 TDD구현 첫 사이클로 생각하며 원인을 파악했고 초기 요구사항을 제대로 파악하지 못했음을 확인했다. 환경 차이를 가볍게 여겨 \n 개행문자와 콘솔 입력의 \n의 차이를 대충 이해하고 인식 조차 못한것이 문제였다 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미션 1주차 부터 바로 &amp;lsquo;요구사항 탐색 도구로서의 TDD&amp;rsquo;를 제대로 경험했다 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경험을 통해 여러 가지를 느꼈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선, TDD 사이클을 반복하는 과정 자체가 요구사항을 놓치지 않도록 나를 계속해서 되묻게 만들고, 단순히 &amp;ldquo;읽고 이해하는 것&amp;rdquo;이 아니라 시스템적으로, 강제적으로 이해하게 만든다는 점이 인상 깊었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 TDD는 단순히 만들면서 테스트하는 것이 아니라, 테스트하면서 요구사항을 발견하는 과정이라는 사실을 체감했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 구현을 먼저 끝내고 테스트를 작성했다면 이번에 발견한 버그는 훨씬 늦게 알아차렸을 것이고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수정에는 지금보다 훨씬 많은 시간이 들었을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작은 단위로 검증하며 나아가기 때문에 큰 실수를 조기에 발견할 수 있다는 점도 크게 와닿았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 기능을 모두 구현한 뒤 테스트를 했다면 어디서 문제가 발생했는지 파악하는 데 지금보다 몇 배의 시간이 더 걸렸을 것이라고 느꼈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 &amp;ldquo;이렇게 입력하겠지&amp;rdquo;라는 내 추측이 아니라, &amp;ldquo;실제로 확인해보자&amp;rdquo;는 태도가 필요하다는 것을 깨달은 점이 가장 인상 깊다 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TDD는 나의 막연한 가정을 줄이고, 확실한 검증 위에서 구현하도록 나를 훈련시키는 도구였다 !!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 3주차 로또 미션 당시 보너스 번호 매칭 로직 버그를 제출 전날 발견하며 상당히 혼란스러워하며 디버깅에 많은 시간을 썼다. 구현을 먼저 하고 테스트를 나중에 작성했기 때문에 요구사항 누락을 뒤늦게 발견할 수 밖에 없었다. 하지만 TDD로 작은 단위씩 검증하며 진행했기에 이스케이프 시퀀스 문제를 조기에 발견할 수 있었다. 통합 테스트 실행 직후 바로 문제를 인지했고, 범위가 좁았기 때문에 원인 파악도 빨랐다 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&amp;ldquo;리팩토링 안전망으로서의 TDD&amp;rdquo;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 작성하던 중 Tell, Don&amp;rsquo;t Ask 원칙을 위반한 점을 발견했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에는 작동에는 문제 없으니 한번에 싹 리팩토링을 진행하자 라고 지나쳤을 상황이지만 이번엔 달랐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구현 과정 중 이미 살아있는 테스트가 있기 때문에, &amp;ldquo;테스트가 있으니 고쳐도 안전하다&amp;rdquo;는 확신이 들었고, 그것이 리팩토링을 시도할 수 있는 용기가 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;StringCalculator 입장에서 Expression을 어떻게 쓰고 싶은가?&amp;rdquo;, &amp;ldquo;Expression에게 어떤 책임을 위임하고 싶은가?&amp;rdquo;를 기준으로 고민했고, 결국 expression.toNumbers()라는 메서드로 역할을 이동시키는 설계를 떠올렸다. 내부 구현은 Expression이 책임지고, StringCalculator는 결과만 사용하도록 구조를 변경하고 싶었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;곧바로 구현하지 않고, 먼저 이 사용 방식을 검증하는 테스트를 작성했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;StringCalculator가 Expression의 내부를 알 필요 없이 사용하도록 expression.toNumbers()로 책임을 이동시키는 구조를 테스트부터 설계했고, TDD 사이클을 통해 점진적으로 리팩토링을 진행했다. 코드 구조는 더 단순해졌고, 객체 간 의존성도 줄어들어 객체의 책임과 캡슐화도 자연스럽게 개선되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변경 후 테스트를 실행했을 때 모든 테스트가 통과하는 순간, &amp;ldquo;사용처를 바꿨는데도 기존 테스트가 모두 통과한다&amp;rdquo;는 사실이 큰 안정감을 주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경험은 로또 미션에서 Service 계층을 도입하기 위해 여러 리팩토링을 하는 과정에서 엄청난 피로를 느낀 기억과 강하게 대비 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당시에는 구현 후에 작성한 테스트가 구조에 종속되어 있었기 때문에 한곳을 바꾸면 여기 저기 깨지는 곳이 많아 불안과 함께 상당한 수정을 하며 스트레스를 받았었다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 이번에는 TDD 방식으로 행위 중심 테스트를 먼저 작성했기 때문에, 구현을 바꿔도 테스트가 무너지지 않았고, 오히려 구조 변경의 안전망 역할을 해주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경험을 통해 TDD 사이클 안에는 리팩토링이 자연스럽고 강제적으로 포함되어 있다는 중요성을 체감하게 되었다. 리팩토링은 선택이 아니라 필수였고, 테스트와 프로덕션 코드를 동시에 발전시키다 보니 테스트에 대한 신뢰가 생겼으며, 그 신뢰가 리팩토링에 대한 두려움을 없애주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 리팩토링은 더 이상 불안한 작업이 아니라, 테스트를 통해 검증 가능한 실험이 되었고, 수정의 족쇄가 아니라 구조 변경을 가능하게 하는 진정한 안전망이 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무엇보다 인상 깊었던 점은, 테스트가 코드의 정확성뿐 아니라 개발자로서의 심리적 안정까지 제공해준다는 것이었다. 생각보다 이런 심리적인 이점이 너무나도 크게 와닿았다 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&amp;ldquo;설계 피드백 도구로서의 TDD&amp;rdquo;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TDD에서의 테스트는 단순한 검증 도구가 아니라 설계에 대한 즉각적인 피드백 도구가 된다는 점을 느꼈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커스텀 구분자 파싱 테스트를 작성하며 Delimiter의 동작을 검증 할때 예상과 달리 서로 다른 객체로서 인식되며 실패 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 같았으면 Delimiter 클래스를 완성한 뒤 당시의 특정 상황에서 오케이 잘 되네 하고 넘어가면서 equals()의 필요성 조차 인식하지 못했을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 TDD로 테스트를 작성하는 순간 &amp;ldquo;비교가 필요한 상황&amp;rdquo;에 즉시 마주했고, 테스트 실패를 통해 설계의 빈틈을 명확하게 발견했다 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과거에는 객체의 역할과 책임만을 머릿속으로만 고민하며 추상적인 수준에서 설계를 시도했다면, TDD에서는 테스트를 먼저 작성하면서 &amp;ldquo;이 객체를 어떻게 사용하게 될까?&amp;rdquo;, &amp;ldquo;실제로 비교가 필요하네?&amp;rdquo; 라는 식으로 설계 요구가 자연스럽게 코드 레벨에서 드러났다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설계가 머리에서 시작되는 것이 아니라, 사용 시나리오를 표현한 테스트 코드로부터 도출되는 과정을 처음으로 체감한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 깨달음이 머릿속 설계 고민이 아니라, 테스트 코드를 작성하는 과정에서 자연스럽게 떠올랐다는 점이 너무 신기했다. 내가 설계를 끌어낸 것이 아니라, 테스트가 설계를 끌어올려 줬다는 느낌을 처음으로 느꼈다 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로 이 경험을 통해 테스트는 버그를 사후에 잡는 도구만이 아니라, 좋은 설계를 사 끌어낼수도 있는 도구라는 점을 경험했다. equals() 구현이라는 작은 사례였지만, 이 경험 하나만으로도 TDD가 설계를 어떻게 바꾸는지, 그리고 왜 테스트를 &amp;ldquo;설계 피드백 도구&amp;rdquo;라고 부르는지 확실히 느낄 수 있었다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&amp;ldquo;TDD 과정에서의 Getter 사용 vs 행위 테스트&amp;rdquo;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TDD를 적용하면서 가장 크게 고민한 점은 Getter를 이용한 상태 검증과 행위 검증 사이의 선택이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기에는 Expression의 파싱 결과를 확인하기 위해 내부 값을 그대로 검증하는 테스트를 작성했다. 그 순간에는 매우 합리적인 선택처럼 느껴졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이후 필드 이름을 조금 더 명확하게 바꾸고자 했을 뿐인데, 테스트가 모두 깨졌고, getter 이름을 어떻게 할지에 대한 고민까지 이어졌다. 필드명과 getter명을 맞추기 위해 getRawNumbers()로 바꾸자니 모든 테스트를 수정해야 했고, 기존 이름을 유지하자니 코드의 일관성이 깨졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;필드명 하나 바꾸는데 테스트를 이렇게 많이 고쳐야 하는 게 맞나? 내가 테스트를 위해 내부 구조를 고정시키고 있는 건 아닐까?&amp;rdquo; 또한 Expression.from()에서 trim() 같은 전처리를 추가하며 내부 구현을 조금 개선했을 뿐인데, 기존 상태 검증 테스트들이 다시 깨졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이떄 명확히 이 테스트는 Expression의 행위를 검증하는 게 아니라, 내부 구현 방식에 매달리고 있었다는걸 꺠달았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Expression의 진짜 목적은 내부 문자열을 저장하는 것이 아니라, 숫자들을 추출해서 Numbers 객체로 만드는 것이었다. 그래서 더 이상 내부 상태를 검증하지 않고, 최종 행위인 toNumbers()의 결과를 검증하는 테스트로 변경했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 변화를 통해 &amp;ldquo;무엇(What)과 어떻게(How)&amp;rdquo;를 구분하는 감각이 확실히 생겼다. 이전 테스트는 &amp;ldquo;어떻게 저장했는가?&amp;rdquo;를 검증하는 테스트였고, 구현 세부사항에 강하게 의존하고 있었으며, 리팩토링에 극도로 취약했다. 반면 변경된 테스트는 &amp;ldquo;무엇을 만들어내는가?&amp;rdquo;를 검증하는 테스트였고, 구현 방식이 바뀌어도 테스트는 그대로 유지되었으며, 캡슐화도 자연스럽게 보장되었다. 이 경험을 통해 잘못된 테스트는 리팩토링을 가로막는 족쇄가 될 수 있고, 올바른 테스트는 리팩토링을 가능하게 하는 날개가 될 수 있다는 사실을 직접 체감했다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&amp;ldquo;Expression 테스트에서 Numbers.from() 사용&amp;rdquo;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Expression 테스트에서 Numbers.from()을 이용해 기대값을 만들게 되면서, &amp;ldquo;이게 정말 순수한 단위 테스트가 맞나?&amp;rdquo;라는 의문이 들기 시작했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;만약 Numbers.from()에 버그가 있다면, expected 값과 actual 값이 동시에 잘못 만들어져서 테스트가 통과하는 건 아닐까?&amp;rdquo;, &amp;ldquo;Expression을 테스트하면서 Numbers에 의존하는 게 맞나?&amp;rdquo;, &amp;ldquo;Mock으로 Numbers를 대체해야 하는 거 아닐까?&amp;rdquo;와 같은 고민이 이어졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 최종적으로 선택한 방식은 고전파스타일의 접근이었다. Numbers는 외부 시스템이 아니라 같은 도메인 레이어의 값 객체이며, Expression의 협력 대상이 아니라 결과물에 가까운 객체라고 판단했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;String이나 Integer를 테스트할 때 Mock으로 대체하지 않는 것처럼, Numbers 역시 실제 객체를 사용하는 것이 더 자연스럽다고 느꼈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 각 객체는 각자의 테스트에서 자신의 책임을 검증하고 있었기 때문에, 신뢰의 체인을 만들 수 있었다. NumbersTest에서 Numbers.from()의 정확성을 검증하고, 그 위에서 ExpressionTest는 &amp;ldquo;Expression이 올바른 Numbers를 생성하는가&amp;rdquo;에 집중하는 구조였다. 하위 테스트가 통과되면 상위 테스트에서 그것을 신뢰하고 사용하는 방식이 오히려 테스트를 더 단순하게 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경험을 통해 모든 의존성을 무조건 Mock으로 격리해야 하는 것은 아니라는 사실도 다시금 깨달았다. DB, 외부 API, 랜덤 값, 시간 같은 외부 의존성은 Mock으로 제어해야 하지만, 도메인 객체는 빠르고 안정적이며 통제 가능하기 때문에 실제 객체를 사용하는 편이 오히려 유지보수성과 가독성 측면에서 유리했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 것은 &amp;ldquo;무엇을 격리해야 하는가&amp;rdquo;를 기술이 아니라 설계 관점에서 판단하는 것이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로 이 과정은 나에게 중요한 감각을 남겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단위 테스트란 무조건 모든 의존성을 제거하는 것이 아니라, 테스트의 목적을 명확히 하고, 신뢰할 수 있는 범위 내에서 의존성을 관리하는 것이라는 사실이었다. 그리고 이 선택 역시, 이론이 아니라 실제 구현과 테스트를 하며 겪은 고민 속에서 체득했기 때문에 훨씬 오래 남을 것 같다고 느꼈다 !!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;124&quot; data-start=&quot;118&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;마치며&lt;/b&gt;&lt;/h2&gt;
&lt;p data-end=&quot;337&quot; data-start=&quot;126&quot; data-ke-size=&quot;size16&quot;&gt;이번 첫 TDD 경험은 단순히 새로운 개발 방법론을 배운 것을 넘어, &lt;b&gt;개발을 바라보는 관점 자체가 달라지는 전환점&lt;/b&gt;이 되었다.&lt;br /&gt;이전까지는 구현 후&amp;nbsp; 테스트의 흐름에 익숙해 있었고, 테스트는 결과를 확인하는 도구라고 생각했지만, 이번 경험을 통해 &lt;b&gt;테스트가 요구사항을 탐색하고, 설계를 이끌며, 리팩토링을 가능하게 하는 핵심 도구&lt;/b&gt;라는 것을 몸으로 느낄 수 있었다.&lt;/p&gt;
&lt;p data-end=&quot;576&quot; data-start=&quot;339&quot; data-ke-size=&quot;size16&quot;&gt;특히 인상 깊었던 것은 TDD가 단순히 코드를 안전하게 만들어주는 기술이 아니라, &lt;b&gt;개발자의 사고 방식과 태도를 변화시키는 훈련 도구&lt;/b&gt;라는 점이었다.&lt;br /&gt;&amp;ldquo;이렇게 입력하겠지&amp;rdquo;라는 추측이 아니라, &amp;ldquo;실제로 어떤 일이 일어나는지 확인해보자&amp;rdquo;는 태도를 갖게 되었고, 불안 속에서 리팩토링을 미루는 개발자가 아니라, 테스트라는 안전망 위에서 &lt;b&gt;의도적으로 구조를 개선하는 개발자&lt;/b&gt;로 한 발 더 나아갈 수 있었다.&lt;/p&gt;
&lt;p data-end=&quot;669&quot; data-start=&quot;578&quot; data-ke-size=&quot;size16&quot;&gt;물론 아직 TDD에 완전히 익숙해졌다고 말하기에는 부족함이 많다.&lt;br /&gt;속도도 느렸고, 중간에 방향을 잃고 헤매기도 했으며, 여전히 매 순간이 선택의 연속이었다.&lt;/p&gt;
&lt;p data-end=&quot;772&quot; data-start=&quot;671&quot; data-ke-size=&quot;size16&quot;&gt;하지만 확실히 이번 경험을 통해 나는 더 이상 &amp;ldquo;TDD를 아는 개발자&amp;rdquo;가 아니라, &lt;b&gt;TDD를 통해 고민하고, 흔들리고, 성장해 본 개발자&lt;/b&gt;가 되었다는 것이다.&lt;/p&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;854&quot; data-start=&quot;774&quot; data-ke-size=&quot;size16&quot;&gt;앞으로도 이 경험을 발판 삼아, 2,3,4주차 미션들로 조금씩 더더 복잡한 도메인, 더 큰 시스템에서도 &lt;b&gt;TDD를 나만의 도구로 쓰는 개발자&lt;/b&gt;로 성장해 나가고 싶다 !!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>외부활동/우아한테크코스 [프리코스]</category>
      <author>softmoca__</author>
      <guid isPermaLink="true">https://softmoca.tistory.com/549</guid>
      <comments>https://softmoca.tistory.com/549#entry549comment</comments>
      <pubDate>Sun, 9 Nov 2025 22:18:28 +0900</pubDate>
    </item>
    <item>
      <title>TDD 핵심 정리</title>
      <link>https://softmoca.tistory.com/548</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. TDD란 무엇인가&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1-1. 정의&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TDD(Test-Driven Development, 테스트 주도 개발)는&amp;ldquo;테스트 코드를 먼저 작성하고, 그 테스트를 통과시키기 위한 최소한의 기능만 구현한 뒤, 리팩토링으로 설계를 다듬어 가는 개발 방식&amp;rdquo;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전통적인 방식(구현 &amp;rarr; 테스트)과 달리 TDD는 순서를 다음과 같이 강제한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;실패하는 테스트를 먼저 작성한다 (Red)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;테스트를 통과시키기 위한 최소한의 코드만 구현한다 (Green)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;중복 제거, 구조 개선, 책임 분리 등의 리팩토링을 수행한다 (Refactor)&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 사이클을 반복하면서,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요구사항을 조금씩 구체화하고,&lt;/li&gt;
&lt;li&gt;설계를 점진적으로 개선하고,&lt;/li&gt;
&lt;li&gt;테스트를 안전망으로 활용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, TDD는 &amp;ldquo;테스트를 작성하는 기술&amp;rdquo;이 아니라 &amp;ldquo;테스트를 매개로 개발 순서를 바꾸는 사고방식&amp;rdquo;이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. TDD의 핵심 사이클: Red &amp;rarr; Green &amp;rarr; Refactor&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-1. Red: 실패하는 테스트 작성&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아직 구현되지 않은 기능에 대해 &lt;b&gt;기대하는 동작&lt;/b&gt;을 코드로 먼저 표현한다.&lt;/li&gt;
&lt;li&gt;이 단계에서 중요한 것은 &amp;ldquo;실패하는 테스트를 만드는 것&amp;rdquo; 자체다.&lt;/li&gt;
&lt;li&gt;이때 테스트는 다음을 명확히 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;어떤 입력을 주었을 때&lt;/li&gt;
&lt;li&gt;어떤 결과/행동이 나와야 하는지&lt;/li&gt;
&lt;li&gt;어떤 예외 케이스를 고려해야 하는지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이 단계에서 사실상 &amp;ldquo;요구사항 명세&amp;rdquo;가 이루어진다.&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-2. Green: 테스트를 통과시키는 최소 구현&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Red에서 작성한 테스트를 &lt;b&gt;통과시키기 위한 최소한의 코드만&lt;/b&gt; 작성한다.&lt;/li&gt;
&lt;li&gt;&amp;ldquo;예쁘게&amp;rdquo; 또는 &amp;ldquo;완벽하게&amp;rdquo; 만들려고 하지 않는다.&lt;/li&gt;
&lt;li&gt;설계를 한 번에 완성하려 하지 않고, &lt;b&gt;우선 동작하게 만드는 것&lt;/b&gt;에 집중한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정에서, 누락된 요구사항이 테스트 실패로 드러나고,불필요한 기능을 미리 만들어두는 일을 줄일 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-3. Refactor: 설계 개선 및 정리&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테스트가 모두 초록색(Green)이 된 상태에서 &lt;b&gt;코드 구조를 개선&lt;/b&gt;한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;중복 제거&lt;/li&gt;
&lt;li&gt;메서드/클래스 분리&lt;/li&gt;
&lt;li&gt;명확한 이름 부여&lt;/li&gt;
&lt;li&gt;책임 분리, 응집도/결합도 개선&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이때 테스트는 &amp;ldquo;안전망&amp;rdquo; 역할을 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리팩토링 후에도 테스트가 모두 통과하면, &lt;b&gt;기존 동작이 깨지지 않았다는 신뢰&lt;/b&gt;를 얻는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;따라서 TDD에서 설계는 처음부터 완벽하게 짜는 것이 아니라,
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;테스트를 통과시키는 과정을 반복하면서 점진적으로 다듬는 것&lt;/b&gt;에 가깝다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. TDD와 요구사항: &amp;ldquo;테스트는 요구사항의 또 다른 표현&amp;rdquo;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3-1. 테스트는 &amp;ldquo;검증 도구&amp;rdquo;가 아니라 &amp;ldquo;요구사항 명세&amp;rdquo;다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 인식:&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 = &amp;ldquo;정답 검증 + 버그 잡는 도구&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TDD 관점:&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 = 요구사항을 코드로 표현한 것&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요구사항 문서/기획서가 자연어(한국어, 영어 등)라면, 테스트 코드는 그 요구사항을 &lt;b&gt;컴퓨터가 이해할 수 있는 형태로 옮겨놓은 것&lt;/b&gt;이다.&lt;/li&gt;
&lt;li&gt;예를 들어, &amp;ldquo;보너스 번호가 기존 당첨 번호와 중복되면 안 된다&amp;rdquo;라는 요구사항은 다음과 같은 테스트로 표현될 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;given 이미 6개의 당첨 번호가 있고&lt;/li&gt;
&lt;li&gt;when 그중 하나와 같은 숫자를 보너스 번호로 입력하면&lt;/li&gt;
&lt;li&gt;then 예외가 발생해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 테스트를 먼저 작성하면 내가 요구사항을 정확히 이해했는지 검증하고, 빠뜨린 케이스가 무엇인지 스스로 질문하게 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3-2. &amp;ldquo;테스트 먼저&amp;rdquo;가 요구사항 탐색을 돕는 이유&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구현을 먼저 하면:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;머릿속 설계/구현 아이디어에 끌려가서
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예외 케이스는 나중으로 미루거나&lt;/li&gt;
&lt;li&gt;&amp;ldquo;일단 돌아가게&amp;rdquo; 한 뒤&lt;/li&gt;
&lt;li&gt;&lt;b&gt;마지막에 한 번에 검증하려는 경향&lt;/b&gt;이 생긴다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이때 요구사항 누락은 대부분 &lt;b&gt;마지막 테스트&lt;/b&gt; 또는 &lt;b&gt;제출 직전/배포 직전&lt;/b&gt;에야 드러난다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트를 먼저 작성하면:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;ldquo;이 기능이 정상일 때&amp;rdquo;뿐 아니라 &lt;b&gt;&amp;ldquo;언제 실패해야 하는지&amp;rdquo;를 먼저 고민&lt;/b&gt;하게 된다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;잘못된 입력&lt;/li&gt;
&lt;li&gt;경계값 (0, 1, 최대값, 최소값)&lt;/li&gt;
&lt;li&gt;중복, 비어있음, null 등&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이 과정에서
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문서에는 없었지만 &lt;b&gt;실제로 필요한 케이스&lt;/b&gt;를 스스로 발견하게 된다.&lt;/li&gt;
&lt;li&gt;예: &amp;ldquo;보너스 번호가 1~45 범위를 벗어나면 어떻게 할까?&amp;rdquo;, &amp;ldquo;보너스 번호가 기존 6개와 중복되면?&amp;rdquo; 등.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, TDD의 &amp;ldquo;Red 단계&amp;rdquo;는 &lt;b&gt;요구사항을 탐색하고, 숨겨진 조건들을 발굴하는 단계&lt;/b&gt;이기도 하다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. TDD와 설계: &amp;ldquo;테스트하기 쉬운 코드 = 잘 설계된 코드&amp;rdquo;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4-1. 테스트가 설계를 이끄는 이유&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TDD에서 테스트는 단순히 결과를 확인하는 도구가 아니라, &lt;b&gt;&amp;ldquo;이 객체를 어떻게 사용하고 싶은지&amp;rdquo;를 먼저 드러내는 역할&lt;/b&gt;을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, LottoResultCalculator를 테스트하며&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;생성자를 어떻게 만들고 싶은지,&lt;/li&gt;
&lt;li&gt;어떤 메서드를 호출하고 싶은지,&lt;/li&gt;
&lt;li&gt;파라미터를 어떤 형태로 넘기고 싶은지가 드러난다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 관점에서 보면 다음이 자연스럽게 보인다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;ldquo;이 객체는 너무 많은 걸 알고 있다&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&amp;ldquo;이 메서드는 파라미터가 너무 많다&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&amp;ldquo;이 객체 없이 테스트하기가 너무 어렵다 (의존성이 꼬여 있다)&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 테스트를 먼저 쓰면 &lt;b&gt;&amp;ldquo;사용하기 좋은 API&amp;rdquo;를 먼저 설계하게 된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정에서:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;의존성 분리가 자연스럽게 진행된다.&lt;/li&gt;
&lt;li&gt;역할과 책임이 테스트 단위에 맞게 분해된다.&lt;/li&gt;
&lt;li&gt;테스트하기 쉬운 구조 = 사용하기 편한 구조임을 체감하게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4-2. 구현 후 테스트의 문제점: &amp;ldquo;이미 만들어진 설계에 테스트를 끼워 맞춤&amp;rdquo;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구현을 먼저 하고 나중에 테스트를 쓰면:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이미 클래스 구조, 메서드 시그니처, 의존성이 다 고정되어 있다.&lt;/li&gt;
&lt;li&gt;테스트는 그것을 &amp;ldquo;사용하는 쪽&amp;rdquo;이 아니라 &lt;b&gt;&amp;ldquo;검증하기 편한 쪽&amp;rdquo;이 아니라 &amp;ldquo;있는 그대로에 맞춰 억지로&amp;rdquo; 짜야 한다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;이때 자주 발생하는 현상:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테스트를 위해 불필요한 getter를 마구 추가&lt;/li&gt;
&lt;li&gt;내부 구현을 들여다보는 테스트 작성 (private 상태를 간접적으로 검증)&lt;/li&gt;
&lt;li&gt;한 테스트가 너무 많은 것을 검증 (하나의 테스트가 여러 책임을 다 검증)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 곧 다음 신호이기도 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;테스트 작성이 너무 힘들다&amp;rdquo; &amp;rarr; &lt;b&gt;설계가 좋지 않다는 강력한 시그널&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TDD는 이 과정을 반대로 뒤집는다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테스트가 쉽게 작성되는 형태로 설계가 자연스럽게 유도되고, 그 과정에서 &amp;ldquo;역할/책임&amp;rdquo; 중심의 설계가 촉진된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. TDD와 리팩토링: &amp;ldquo;테스트는 족쇄가 아니라 안전벨트&amp;rdquo;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5-1. 테스트가 리팩토링에서 제공하는 것&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리팩토링의 본질: &amp;ldquo;동작은 그대로 유지한 채, 내부 구조만 개선하는 것&amp;rdquo;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;ldquo;이렇게 고쳤다가 어디선가 깨지는 거 아닌가?&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&amp;ldquo;이 코드를 지워도 괜찮을까?&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&amp;ldquo;이 책임을 옮겼는데, 부작용이 없을까?&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TDD에서의 테스트는 이 두려움을 줄여준다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테스트가 모두 통과한다면 최소한 테스트가 표현하고 있는 &lt;b&gt;요구사항 수준에서는 동작이 유지되었다&lt;/b&gt;는 확신을 준다.&lt;/li&gt;
&lt;li&gt;더 과감하게 메서드 분리, 클래스 추출, 의존성 역전 등을 시도할 수 있다.&lt;/li&gt;
&lt;li&gt;&amp;ldquo;감&amp;rdquo;이 아니라 &lt;b&gt;테스트 결과에 근거해 판단&lt;/b&gt;하게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5-2. &amp;ldquo;구현 후 테스트&amp;rdquo;가 리팩토링에서 취약한 이유&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구현 후 테스트를 작성하는 경우:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테스트가 &lt;b&gt;구현에 매우 강하게 의존&lt;/b&gt;하는 형태로 작성되기 쉽다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;내부 메서드 호출 순서를 검증&lt;/li&gt;
&lt;li&gt;특정 필드/구조에 의존&lt;/li&gt;
&lt;li&gt;세부 구현 방식을 가정한 테스트 작성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 테스트는 &lt;b&gt;조금만 구조를 바꿔도 쉽게 깨진다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제 요구사항은 만족하고 있음에도, &amp;ldquo;내부 구현 방식이 바뀌었다&amp;rdquo;는 이유만으로 테스트가 붉게 깨진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 결과: 테스트 = &amp;ldquo;움직이면 깨지는 유리 장식품&amp;rdquo;처럼 느껴지고,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;개발자는 &amp;ldquo;테스트 고치는 것에 지쳐서&amp;rdquo; 리팩토링을 포기하거나 최소한으로만 진행하게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TDD가 지향하는 테스트는:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;내부 구현(How)이 아니라 겉으로 드러나는 행동(What)에 의존&lt;/b&gt;한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입력과 출력, 상태 변화, 외부로 보이는 효과를 검증&lt;/li&gt;
&lt;li&gt;내부 메서드 호출 순서, 세부 알고리즘에는 덜 의존&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이런 테스트는 구현 변경에는 비교적 강건하고, 요구사항이 변하지 않는 한 그대로 살아남는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. TDD에서 &amp;ldquo;테스트 작성 시점&amp;rdquo;의 의미&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 자료 조사의 핵심 중 하나는 &amp;ldquo;테스트를 언제 쓰느냐에 따라, 그 역할이 완전히 달라진다&amp;rdquo;는 점이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6-1. 구현 후 테스트(테스트 레이트, Test-Last)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이미 기능이 완성된 뒤에그 기능이 잘 돌아가는지 확인하기 위해 테스트를 추가한다.&lt;/li&gt;
&lt;li&gt;최소한 &amp;ldquo;큰 버그&amp;rdquo;를 잡는 데 도움은 된다.&lt;/li&gt;
&lt;li&gt;회귀 테스트(기능이 나중에 깨지지 않았는지 확인)에 일정 역할을 한다.&lt;/li&gt;
&lt;li&gt;요구사항 누락을 막기 어렵다.&lt;/li&gt;
&lt;li&gt;설계에 대한 피드백을 줄 수 없다. (이미 설계가 끝난 후이기 때문)&lt;/li&gt;
&lt;li&gt;구현과 강하게 결합된 테스트가 되기 쉽다.&lt;/li&gt;
&lt;li&gt;리팩토링 시 테스트와 구현이 함께 무너질 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6-2. 테스트 후 구현(테스트 퍼스트, Test-First, TDD)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기능 구현 &lt;b&gt;전에&lt;/b&gt; 원하는 동작을 테스트로 먼저 표현한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장점:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;요구사항 누락을 사전에 발견하기 쉽다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;테스트를 기준으로 설계를 고민하게 되어, 구조가 자연스럽게 개선된다.&lt;/li&gt;
&lt;li&gt;구현 세부사항에 덜 의존하는, &lt;b&gt;행위 기반 테스트&lt;/b&gt;를 작성하게 된다.&lt;/li&gt;
&lt;li&gt;테스트가 리팩토링의 안전망 역할을 강하게 수행한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일한 &amp;ldquo;테스트 코드&amp;rdquo;라 해도 &lt;b&gt;언제 작성하느냐에 따라&lt;/b&gt; 단순 검증 도구 vs 요구사항 탐색 &amp;amp; 설계 유도 &amp;amp; 리팩토링 안전망으로 역할이 극명하게 갈린다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 좋은 테스트의 특징: 구현 의존 X, 행위 중심 O&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TDD 문맥에서 &amp;ldquo;좋은 테스트&amp;rdquo;란, 흔히 다음과 같은 특징을 가진다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;요구사항을 드러낸다.&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;읽기만 해도 &amp;ldquo;아, 이 기능은 이렇게 동작해야 하는구나&amp;rdquo;를 이해할 수 있다.&lt;/li&gt;
&lt;li&gt;테스트 이름이 구체적이고, 도메인 언어를 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;하나의 테스트가 한 가지 행동(책임)만 검증한다.&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실패했을 때 &amp;ldquo;어느 부분이 잘못되었는지&amp;rdquo; 빠르게 파악할 수 있다.&lt;/li&gt;
&lt;li&gt;여러 로직을 한 번에 검증하는 거대한 테스트를 피한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;내부 구현보다 &amp;lsquo;겉으로 보이는 결과&amp;rsquo;를 검증한다.&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 메서드가 몇 번 호출됐는가보다, &amp;ldquo;이 입력에 대해 이런 결과가 나왔는가?&amp;rdquo;에 집중한다.&lt;/li&gt;
&lt;li&gt;상태 변화, 리턴 값, 외부 효과(출력 등)를 중심으로 본다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;테스트 코드 자체도 리팩토링 가능해야 한다.&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;중복 제거, 픽스처 정리, 헬퍼 메서드 추출 등을 통해 테스트 코드도 읽기 좋게 관리한다.&lt;/li&gt;
&lt;li&gt;테스트 코드 품질이 떨어지면, 결국 테스트를 신뢰하지 않게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TDD는 이 &amp;ldquo;좋은 테스트&amp;rdquo;를 &lt;b&gt;강제로라도 먼저 작성하게 만드는 시스템&lt;/b&gt;이라고 볼 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. 자주 하는 오해와 함정&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;&amp;ldquo;테스트 코드가 있으면 버그가 안 생긴다&amp;rdquo;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제로는 &amp;ldquo;테스트가 표현하고 있는 범위 안에서만&amp;rdquo; 버그를 막아준다.&lt;/li&gt;
&lt;li&gt;잘못 이해한 요구사항, 빠뜨린 케이스는 여전히 버그로 남는다.&lt;/li&gt;
&lt;li&gt;그래서 &lt;b&gt;요구사항 탐색 과정으로서의 테스트 작성이 중요&lt;/b&gt;하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&amp;ldquo;테스트 코드가 있으면 리팩토링이 쉽다&amp;rdquo;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;구현에 강하게 결합된 테스트는 리팩토링 시 같이 무너진다.&lt;/li&gt;
&lt;li&gt;TDD는 &amp;ldquo;행위 중심 테스트&amp;rdquo;를 유도해서 이 문제를 완화한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&amp;ldquo;테스트 코드는 설계를 자동으로 개선해준다&amp;rdquo;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테스트를 그냥 &amp;lsquo;나중에&amp;rsquo; 작성하면 설계는 바뀌지 않는다.&lt;/li&gt;
&lt;li&gt;TDD는 &amp;ldquo;테스트 &amp;rarr; 구현 &amp;rarr; 리팩토링&amp;rdquo; 순서를 강제하면서 설계 개선의 루프를 만들어 주는 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>외부활동/우아한테크코스 [프리코스]</category>
      <author>softmoca__</author>
      <guid isPermaLink="true">https://softmoca.tistory.com/548</guid>
      <comments>https://softmoca.tistory.com/548#entry548comment</comments>
      <pubDate>Sat, 8 Nov 2025 12:46:04 +0900</pubDate>
    </item>
  </channel>
</rss>