<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Rudy DevNote  </title>
    <link>https://kimjoraeng.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Sun, 5 Apr 2026 14:53:37 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>_루디_</managingEditor>
    <image>
      <title>Rudy DevNote  </title>
      <url>https://tistory1.daumcdn.net/tistory/5638416/attach/ae929cfcb32140dc80b8cbc1aad7d5b3</url>
      <link>https://kimjoraeng.tistory.com</link>
    </image>
    <item>
      <title>[회고] 우아한테크코스 8기 백엔드 레벨1 2주차 회고</title>
      <link>https://kimjoraeng.tistory.com/89</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;우테코 2주차(3.2~3.6) 회고&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;2주차 페어 프로그래밍&lt;/b&gt;이 시작되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;솔직히 나는 최종 코테 이후로 이사 준비로 바빠서 자바 코딩을 손 놓고 있었고, 그 이후 손까지 다치면서 페어 미션을 진행하는 동안에 제대로 타이핑을 못해서 페어에게 폐를 끼치는 것은 아닐까 하는 걱정을 하고 있었는데, 어떻게든 목표로 한 기간 내에 완성하여 PR을 제출할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 미션에서는 본격적으로 미션에 대해 함께 설계하고, 서로의 작업 방식을 공유하고 번갈아가면서 코드도 작성하는 우테코의 전통적인 페어 미션과 동일한 방식으로 진행이 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로 프리코스를 경험하면서 설계에 너무 많은 시간을 투자하고 완벽한 설계에 집착하다 보니 피곤했던 점도 있고 해서 이번에는 페어의 의견에 맞춰가면서 새로운 방식으로 설계하고 개발하는 과정을 경험해보고자 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 우테코에서도 빠른 PR 제출을 통해 많은 피드백을 받고 지속적으로 개선시키는 과정을 추구하기 때문에 이를 의식해서도 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PR을 제출하면서 리뷰어에게 질문을 할 수 있었기에 개인적으로 계속 고민해봤지만 답을 내기 어려웠던 점들에 대해서 질문하고 답을 받았다. 정답을 알려주는 느낌이라기보다 학습 방향에 대한 기준을 잡을 수 있는 답변이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Q. 우테코에서는 빠르게 PR을 보내는 것이 항상 더 좋은 선택일까요?&lt;br /&gt;조금 더 고민해서 코드 퀄리티를 높인 뒤 보내는 것이 맞는지 고민이 됩니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;우테코가 개인 사이드 프로젝트와 다른 점은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;현업에서 일하는 개발자에게 피드백을 받을 수 있는 환경&lt;/b&gt;이라는 점이라는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;빠르게 PR을 제출하면 혼자 오래 고민하던 부분에 대해 어떤 방향으로 생각하면 좋을지, 어떤 부분을 더 고민해 보면 좋을지에 대해 빠르게 피드백을 받을 수 있습니다. 혼자 고민하는 시간보다 더 넓은 관점을 얻을 수 있다는 점이 장점이라고 생각합니다&lt;/span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 것은 &lt;b&gt;PR을 얼마나 빨리 보냈는지 자체가 아닙니다.&lt;/b&gt;&lt;br /&gt;그 과정에서 &lt;b&gt;얼마나 고민했고, 무엇을 배웠는지&lt;/b&gt;가 더 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빠른 PR은 피드백을 더 받을 수 있다는 장점이 있지만, 단순히 속도만을 목표로 하기보다는&lt;br /&gt;&lt;b&gt;학습과 이해를 중심에 두고 PR을 보내는 것이 더 중요합니다.&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;Q. 미션이나 사이드 프로젝트를 시작할 때 설계를 어느 정도까지 해두고 시작하는 것이 좋을까요?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A. 어떤 부분에서 문제가 발생할 수 있는지, 무엇을 미리 결정해야 하는지를 &lt;b&gt;이전 경험을 통해 어느 정도 알고 있다면&lt;/b&gt;&lt;br /&gt;설계의 비중을 조금 더 가져가면서도 효율적으로 개발할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이런 경험이 충분하지 않은 상태에서는 처음부터 완벽한 설계를 만드는 것은 어렵기 때문에 &lt;b&gt;학습 단계인&amp;nbsp;지금은 완벽한 설계에 집착하기 보단 자신만의 기준을 찾아나가는 시간으로 생각하면 좋을 것 같다.&amp;nbsp;&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;Q. 학습 과정에서 AI를 사용하는 것에 대해서는 어떻게 생각하시나요?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A. &lt;b&gt;AI를 사용했다는 사실 자체는 중요하지 않습니다.&lt;/b&gt;&lt;br /&gt;더 중요한 것은 &lt;b&gt;AI가 제시한 답을 내가 이해했는지, 그리고 그것을 설명할 수 있는지&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI의 답을 그대로 사용하는 것이 아니라&lt;br /&gt;&lt;b&gt;이해하고 자신의 것으로 만드는 과정이 동반된다면 학습 도구로 활용하는 것도 의미가 있습니다.&lt;/b&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로 혼자 고민해봐도 답을 내기가 어려웠는데 정답에 대한 제시가 아니라 생각을 정리하는데 도움이 되는 경험이나 생각을 많이 공유해 주셔서 내 스스로 답을 내고 앞으로의 방향성을 잡는데 큰 도움이 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리뷰어님이 적어주신 대로 우테코에서의 큰 장점은 현업에서 일하는 개발자에게 코드의 리뷰를 받고 개인적인 고민도 물어볼 수 있는 환경이라는 것이 너무 중요한 점인 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 겨우 첫 미션인데, 앞으로의 미션을 진행하면서도 많이 배우고 좋은 대화를 많이 나눌 수 있었으면 좋겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 2주차에서 부족했던 코드는 3주차에서 다시 개선하면 되니까 ...&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;&amp;nbsp;&lt;/p&gt;</description>
      <author>_루디_</author>
      <guid isPermaLink="true">https://kimjoraeng.tistory.com/89</guid>
      <comments>https://kimjoraeng.tistory.com/89#entry89comment</comments>
      <pubDate>Mon, 16 Mar 2026 17:56:57 +0900</pubDate>
    </item>
    <item>
      <title>[회고] 우아한테크코스 8기 백엔드 레벨1 - 1주차 회고</title>
      <link>https://kimjoraeng.tistory.com/88</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;우아한 테크코스 1주 차(2.24~27)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에도 뒤늦게 남기게 된 회고이다..&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몇 주 전 요리하다 손가락을 다치는 바람에 타자 치는 게 불편해져서 타자 칠 일이 많은 회고 글 작성을 최대한 미루고 미루다가 손 끝 통증이 거의 없어지면서 다시 써볼 엄두를 낼 수 있게 되었다..&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1주 차는 정신없이 지나갔다. 처음 들어와서 어색한 크루원들과의 적응도 있었지만 졸업식, 이사 준비 등이 겹치면서 정신없이 지내면서 개발을 놓고 있었는데, 교육장에 들어와서 개발자로서의 삶으로 돌아오는 재활 시간으로 보낸 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 8기부터 새로운 교육장에서 교육을 받게 되어 판교 제2 테크노밸리의 교육장에서 교육이 진행되는데, 새 건물에 모든 시설이 새삥이라 구경하는 맛이 있었다.&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;처음 도착하고 OT를 받으면서 앞으로의 생활에 대해 설명을 받고 가장 인상 깊었던 점은 우테코에서는&amp;nbsp;&lt;b&gt;소프트 스킬&lt;/b&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;연극&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소프트 스킬의 온보딩 미션으로 연극이 주어졌다 (...)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연극 팀원은 총 5명, 각자 역할을 맡아 특정 주제를 전달하는 무대를 완성하는 것이 목표이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연극 조원들과 모여 다양한 아이디어를 주고받았고, 각자 적극적인 아이디어를 낸 덕에 생각보다 쉽게 아이디어를 정리하고 주제를 정해서&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;한 사람 씩 돌아가면서 각 장소별 청소 체크 리스트를 전달하면서 뒤에 조원들은 실제로 청소하는 과정을 연기하고 있을 때,&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;솔직히 이런 무대를 해본 적도 없는 개발자들이 모여서 무대를 준비한다는 게 정말 힘든 일이었지만&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;다 해낼 수 있을 것 같은 그런 느낌이 들었다 ^_^.. (p.s 회의 중 제일 많이 나왔던 말은 &quot;우테코 여기까지만 할까..&quot;였다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;첫 페어프로그래밍&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미션 외에는 첫 페어프로그래밍이 진행되었는데, 기존 우테코의 페어 미션과는 다르게 이번에는 처음부터 AI만을 활용한 페어 프로그래밍으로 진행되었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페어로 미션을 진행하는 경험을 하면서도, AI 시대에 맞춰 어떻게 적응할 것인지 어떤 능력이 필요한지를 체험해 보는 느낌의 활동이었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gemini Canavas 서비스를 사용해서 진행되었는데, 먼저 공식 문서를 읽으면서 서비스에 대해 이해하는 시간을 가졌다.&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;공식 문서를 1순위로 여길 것&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;이번 미션을 시작할 때 코치 준은 공식 문서 제목에 있는 Canvas를 보면서 왜 Canvas라고 했을까에 대한 질문을 던졌고 이에 대한 크루들의 의견도 물어보면서 함께 문서를 읽었다. 확실히 스스로에게 왜 그럴까에 대한 질문을 던지면서 읽는 과정이 이해도 잘되고 기억에도 잘 남는 것을 경험할 수 있었고 앞으로 문서나 정보 글을 읽을 때 써먹어 봐야겠다고 생각했다.&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;공식 문서는 제품을 만든 사람이 고민 끝에 작성한 1차 자료이며, 업데이트 시 가장 먼저 수정되는 자료이기에 업데이트가 자주 이루어지는 인기 서비스를 사용할 때에는 특히나 공식 문서를 읽는 방법을 알고 있는 것이 중요했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 문서를 어렵게 느껴서 누군가가 정제해서 전달하는 블로그나 책에 자꾸 의존하게 되곤 하는데, 공식 문서를 읽고 학습하는 과정에 익숙해지기 위해서 의식적으로 더 노력을 해야 할 것 같다.&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;이렇게 문서를 통해 이해한 내용으로 보면 Gemini Canvas는 만들고 싶은 주제에 대해 기능적인 명세를 잘 정리하여 전달하면, 실제로 눈으로 바로 확인할 수 있는 프로토타입 웹앱을 직접 만들어서 보여줄 수 있는 서비스라고 이해했다. 실제 코드를 작성하거나 AI가 작성해 준 코드를 실행, 배포할 수 있는 환경에 직접 세팅하고 실행하지 않아도 바로 캔버스 환경에서 실행시키고 실행 화면을 이용할 수 있게 해 주며 링크를 통해 공유도 가능하다.&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;이 서비스를 통해 우리는 다음의 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;1. 유틸리티 앱 : 평소 불편했던 점을 해소하기 위한 앱&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 학습 앱 : 우테코 크루들이 자신의 분야를 학습할 때 도움이 될만한 앱&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 게임 : 평소 만들어보고 싶었던 게임&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 페어 릴레이 앱 : 페어와 함께 고민하여 함께 사용할만한 앱&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 웹 사이트 정도만 만들 수 있나? 했는데 결국 크롬 익스텐션도 화면을 구성하는 요소는 모두 웹을 구성하는 html, css, js이고 이를 동작하기 위한 구조도 gemini canvas가 충분히 퀄리티 좋게 완성해주었다. 물론 실제로 크롬에 등록하고 익스텐션으로서 동작하는 과정은 내가 직접 세팅해야 했지만 실제 코드는 건드린 게 하나도 없었다.&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;다음으로 학습 앱은, SQL 쿼리 빌더를 만들었는데 SQL 문이 인덱싱, 캐싱, JOIN 등 DB를 최적화하기 위한 옵션이 얼마나 붙느냐에 따라 실제로 어느 정도의 성능 개선이 이루어지는지 눈으로 직관적으로 보여주기 위해 각 옵션을 직접 체크하며 SQL 쿼리를 돌린 상황을 시뮬레이션하고, 옵션 적용 전 후 DB 전송 비용 등을 시각적인 그래프로 직접 확인하고 비교할 수 있는 서비스였다.&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;개인적으로 DB의 성능 최적화에 대해 배우기도 했고 실제로 사용하면서 고민도 해봤지만 이걸 적용했을 때 직관적으로 얼마나 개선이 되는지 눈으로 확인해보고 싶어서 만들어보았다.&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;/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;마지막으로 페어와의 릴레이 앱에서는 내 의견으로 링크(url)를 카테고리 별로 저장하고 어떤 내용의 링크인지 AI를 통해 요약 소개글을 받아와서 함께 저장할 수 있도록 하는 링크 아카이빙 앱을 만들었다.&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;내가 직접 프롬프트를 작성하지 못하는 상황이 답답하게 느껴질 때도 있었지만, 그만큼 말을 하는 스킬이나 프롬프트를 어떻게 구성하면 좋을지에 대한 생각을 하는 시간을 가지면서 지식을 얻을 수 있었다고 생각한다. 결국 개발자로서 혼자 평생 개발할 것이 아닌 이상 개발에 대한 의도나 진행 상황에 대한 공유, 의견 전달 등을 말을 통해 진행해야 하는데 페어 프로그래밍을 통해 간접적으로 그런 경험을 하게 되고 능력을 기를 수 있는 계기가 된 것 같다.&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;h4 data-ke-size=&quot;size20&quot;&gt;프롬프트에 대한 개인적인 노하우&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gemini Canvas를 주야장천 사용하면서 프롬프트를 작성하는 방법에 대한 나만의 노하우 같은 것도 몇 가지 생긴 것 같다.&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;1. 누구나 이해할 수 있게 명확하고 구체적인 레퍼런스를 제공할 것&amp;nbsp;&lt;/b&gt;&lt;br /&gt;: 내 머릿속에서 생각한 내용을 AI가 귀신같이 알아채고 만들어 준다면 좋겠지만 실제로는 잘 이루어지지 않아 많은 수정을 시도해야 했고, 그 과정에서 콘텍스트가 길어지면서 구현 정확도가 떨어졌다. 처음부터 완성도 높은 결과물을 뽑아내려면 누가 들어도 확실하게 연상이 가능하도록 디테일한 레퍼런스를 함께 줄 때 완성도 높은 결과물을 낼 수 있었다.&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;&lt;b&gt;2. 디버깅을 할 때에는 원인을 파악하고, 대안을 제시할 것&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: 단순히 내 의도랑은 다르니까 고치라고 한다거나, 이게 잘 안된다고 하는 것은 문제를 해결하는데 별 도움이 되지 않았다.&lt;br /&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;&lt;b&gt;3. 모호한 표현은 자제하고, 프롬프트를 구조적으로 잘 정리해서 전달할 것&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;그리고 말을 할 때 사람에게 문자를 보내듯이 구어체로 길게 장황하게 설명하면 꼭 체크하지 못하고 빼먹는 포인트들이 존재했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;md 형식에 맞춰서 기술 명세서를 작성하듯 원하는 요구사항을 카테고리별로 리스트 형태로 정리하여 전달하면 완성도 높은 결과물을 낼 수 있었다.&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;소감&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1주 차를 보내면서 느낀 점이 몇 가지 있는데,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째는 우테코는 지식에 대해 직접적으로 전달하는 주입식 강의는 많지 않다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 이곳에 오면 자바던 스프링이던 기술적인 지식도 강의 형식으로 가르칠 것이라고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 학습을 하기 위한 미션과 방향을 제시하고 환경을 조성해줄뿐 실제로 학습하는 주체는 나 자신이며,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우테코는 그 과정에서 도움이 필요하거나 할 때 도움을 주는 존재에 가깝다고 느꼈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 학습을 어떻게 해야 하는지 방향을 제시해 주고 그것을 맘껏 시도할 수 있는 환경을 제공해 주는 것만으로도 충분히 만족스러웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 누군가 나를 직접 끌어주거나, 강요하지 않기 때문에 이 자유로운 환경에서 목표를 잃고 방황하지 않도록 나 스스로 계획을 잘 세우고 내게 무엇이 부족한지 어떤 능력을 더 끌어올려야 하는지 나 자신에 대해 잘 파악해서 꾸준히 나아가야 한다는 사실을 계속 되뇌어야 할 것 같다.&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;두 번째는 위에도 적었지만 소프트 스킬의 비중이 생각보다 높다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캘린더로 일정을 보았을 때 백엔드 활동과 소프트 스킬의 비중이 1:1 혹은 소프트 스킬이 더 많아 보일 정도였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 코드를 작성하는 백엔드 활동에서 조차 페어로 진행하거나, 리뷰어와 소통을 해야 하기 때문에 소프트 스킬이 필요하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떻게 보면 우테코에 와서 얻을 수 있는 가장 큰 무기가 아닐까 생각한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;지금까지&amp;nbsp;&amp;lsquo;무엇&amp;rsquo;을&amp;nbsp;배울지에만&amp;nbsp;몰두하고&amp;nbsp;&amp;lsquo;어떻게&amp;rsquo;&amp;nbsp;내&amp;nbsp;것으로&amp;nbsp;만들어야&amp;nbsp;할&amp;nbsp;지&amp;nbsp;깊이&amp;nbsp;고민해보지&amp;nbsp;못했는데&amp;nbsp;이번&amp;nbsp;1주차&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;1주차가&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;/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>My Story/우아한테크코스</category>
      <author>_루디_</author>
      <guid isPermaLink="true">https://kimjoraeng.tistory.com/88</guid>
      <comments>https://kimjoraeng.tistory.com/88#entry88comment</comments>
      <pubDate>Mon, 16 Mar 2026 17:26:40 +0900</pubDate>
    </item>
    <item>
      <title>[회고] 우아한 테크코스 백엔드 8기 프리코스 - 오픈미션부터 최종 코테 합격까지</title>
      <link>https://kimjoraeng.tistory.com/87</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3주 차 회고를 작성한 이후로, 오픈미션을 진행하면서부터는 회고글을 작성하는 걸 미루다 보니 최종 코테를 거쳐 합격 결과까지 나온 다음에서야 글을 쓴다. 계속 어떻게 글을 써볼지 고민했는데 앞선 1~3주 차도 마찬가지고 오픈미션에 대해 무엇을 적을지 고민할 때에도 많은 내용을 담으려고 하다 보니 작성하기 전부터 지쳐서 포스팅을 시작하는 걸 꺼리게 되는 것 같다. 그래서 뇌를 비우고 그 당시의 기록을 남긴다는 생각으로 작성해 보려고 한다.&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;1. 오픈미션&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;446&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c0MOLs/dJMcahi0MxE/hGlKahzJiSNaoVyoGtftf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c0MOLs/dJMcahi0MxE/hGlKahzJiSNaoVyoGtftf1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c0MOLs/dJMcahi0MxE/hGlKahzJiSNaoVyoGtftf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc0MOLs%2FdJMcahi0MxE%2FhGlKahzJiSNaoVyoGtftf1%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;403&quot; height=&quot;257&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;446&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오픈미션은 8기부터 처음 도입된 마지막 주차의 과제이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 우테코 마지막 과제는 많은 요구사항을 담고 규모가 클 뿐 1~3주차 동안 진행해 온 과제와 유사하게 진행되었다.&lt;br /&gt;(이전 주차에서 지켜야 할 요구사항이 늘어나고, 좀 더 복잡한 규칙의 프로그램을 설계하는 과정)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 기존의 방식으로는 &lt;b&gt;프리코스에 얼마나 열심히 참여하였는지 보다는 우테코 지원 이전에 어떤 준비를 해왔는지가 합격을 결정&lt;/b&gt;하는 비중이 컸기 때문에 우테코에서의 &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;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;483&quot; data-origin-height=&quot;155&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1dXrL/dJMcaf6xKbh/e1SZLrAR9JkDpbVZUH2KWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1dXrL/dJMcaf6xKbh/e1SZLrAR9JkDpbVZUH2KWk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1dXrL/dJMcaf6xKbh/e1SZLrAR9JkDpbVZUH2KWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1dXrL%2FdJMcaf6xKbh%2Fe1SZLrAR9JkDpbVZUH2KWk%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;374&quot; height=&quot;120&quot; data-origin-width=&quot;483&quot; data-origin-height=&quot;155&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;719&quot; data-origin-height=&quot;427&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBwbA5/dJMcadgz4Cv/ZxLJROKRnes4imFd1KRH8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBwbA5/dJMcadgz4Cv/ZxLJROKRnes4imFd1KRH8K/img.png&quot; data-alt=&quot;오픈미션 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBwbA5/dJMcadgz4Cv/ZxLJROKRnes4imFd1KRH8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBwbA5%2FdJMcadgz4Cv%2FZxLJROKRnes4imFd1KRH8K%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;567&quot; height=&quot;337&quot; data-origin-width=&quot;719&quot; data-origin-height=&quot;427&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;오픈미션 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적으로는 이렇게 3가지 예시를 주셨고, 그 외에도 다양한 예시를 들어주셨다. 바리스타 자격증을 따는 것도 도전이라는 측면에서 인정된다고 하신 만큼, 개발에 국한되지 않고 &lt;b&gt;스스로 도전하고자 하는 문제를 정의하고 해결하는 과정에 몰입하는 모습&lt;/b&gt;을 보이는 것이 중요했던 것 같다. 설명회를 듣고 나서 &lt;b&gt;몰입&lt;/b&gt;과 &lt;b&gt;도전&lt;/b&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;/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기까지는 5주차로 종료되었지만 &lt;b&gt;오픈 미션은 3주라는 기간이 주어져 6주 차까지 진행하게 되었다&lt;/b&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;1.1 준비 과정&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;오픈미션 1주차 - 주제선정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 시작할 때 주제를 뭘로 해야 할지 도저히 감이 안 잡혔다. 너무 자유로운 상태였기 때문에 자격증을 딸지 원래 개인적으로 하던 사이드 플젝을 가져와야 할지, 게임 개발 처럼 해본 적 없는 분야를 도전해 보는 게 좋을지 고민했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 오픈미션이 시작되고 한동안 &lt;b&gt;많은 참가자들이 협업을 선택하고 협업할 파트너를 구하는 글도 디코에 많이 올라왔었다.&lt;/b&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;하지만 나는 미션 주제를 선택할 때 제약이 많았다. 당시 출퇴근을 하면서 프리코스에 임하고 있었기 때문에 시간적인 제약이 많았다. 기껏해야 출퇴근 시간 폰이나 태블릿으로 서칭 좀 해보고, 점심 시간 30분 써서 태블릿으로 코딩을 하고, 퇴근 후 4-5시간가량 컴퓨터 앞에 앉아서 개발하는 것 말고는 현실적으로 더 많은 시간을 투자할 수는 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;남들은 온종일을 쏟아서 참가하는 사람도 많을텐데 출퇴근을 하느라 시간을 적게 쓰면서 팀원에게 민폐를 끼칠 수는 없었기 때문에 협업은 포기, 거창한 주제를 정하기에는 마찬가지로 시간 부족이 문제였다.&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;u&gt;&lt;b&gt;낯선 언어를 활용한 개발&lt;/b&gt;&lt;/u&gt;을 해보기로 했다.&amp;nbsp;그나마 &lt;b&gt;출퇴근 시간을 활용해 문서를 읽거나 영상을 보면서 이론적인 공부를 할 수 있고, 집에 도착하면 4~5시간 집중해서 작업할 수 있는 주제&lt;/b&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;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;369&quot; data-origin-height=&quot;137&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZbKL6/dJMcaaRJa3Q/3l5KSKrQHBYTbd60TGbr90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZbKL6/dJMcaaRJa3Q/3l5KSKrQHBYTbd60TGbr90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZbKL6/dJMcaaRJa3Q/3l5KSKrQHBYTbd60TGbr90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZbKL6%2FdJMcaaRJa3Q%2F3l5KSKrQHBYTbd60TGbr90%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;369&quot; height=&quot;137&quot; data-origin-width=&quot;369&quot; data-origin-height=&quot;137&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 언어로는 Go를 선택했다. Go를 선택한 이유는 과거부터 백엔드 개발 분야에서 언급이 되는 것을 보고 언젠가 꼭 배워보고 싶다고 생각한 언어였는데, 이참에 찾아보니 Go 언어의 철학과 방향성이 자바와 겹치는 부분도 일부 있지만 완전히 반대되는 부분이 많았고 그 특징들이 인상적이라 새로운 사고를 확장하는데 도움이 될 것 같았다.&lt;br /&gt;(&lt;a href=&quot;https://1ambda.github.io/golang/golang-tutorial/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://1ambda.github.io/golang/golang-tutorial/&lt;/a&gt;&amp;nbsp; &amp;lt;&amp;lt; 이 튜토리얼을 보고 결정하였다.)&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;b&gt;성장했던 과정이 도움이 많이 되었다.&lt;/b&gt; 나중에 새로운 언어나 도구를 학습할 때에도 이 방식을 적용해 보면 좋겠다고 생각하고 있었는데 오픈 미션 덕에 Go 학습에 써먹을 수 있게 되었다.&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;Go로 무엇을 만들지도 고민을 많이 해보았는데, 고민하다 문득 무엇을 만들지는 별로 중요하지 않겠다는 생각이 들었다. 이번 미션의 목표는 Go의 철학을 이해하고 Go를 도구로 사용해서 원하는 결과물을 만드는 과정을 경험하는 것이니 그것에 집중하는 게 좋겠다고 생각했다. 그리고 &lt;b&gt;자바와의 차이를 비교하면서 학습하기 위해 기존에 자바로 구현했던 미션을 새로운 언어로 재구현&lt;/b&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;b&gt;Go로 3주 차 로또 시뮬레이션 재구현하기였다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 단순히 동작하기만 하는 프로그램을 만들기보다는 'Go 스럽게' 완성해야 이 주제를 하는 의미가 있다고 생각했다. 로또 주제의 요구사항과 기존 프리코스 과제에서 받은 피드백 사항들을 다시 한 줄 한줄 읽어가면서 유지할 항목, 수정할 항목, 추가할 항목 등을 정리했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;583&quot; data-origin-height=&quot;269&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bK8YFY/dJMcacBZlgk/mPfKx5JpmjNOyMiP4nAFa0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bK8YFY/dJMcacBZlgk/mPfKx5JpmjNOyMiP4nAFa0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bK8YFY/dJMcacBZlgk/mPfKx5JpmjNOyMiP4nAFa0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbK8YFY%2FdJMcacBZlgk%2FmPfKx5JpmjNOyMiP4nAFa0%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;388&quot; height=&quot;179&quot; data-origin-width=&quot;583&quot; data-origin-height=&quot;269&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;538&quot; data-origin-height=&quot;575&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTyGX9/dJMcahXBkFs/fzuqoR7K7cLlxnakJSnPK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTyGX9/dJMcahXBkFs/fzuqoR7K7cLlxnakJSnPK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTyGX9/dJMcahXBkFs/fzuqoR7K7cLlxnakJSnPK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTyGX9%2FdJMcahXBkFs%2FfzuqoR7K7cLlxnakJSnPK1%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;378&quot; height=&quot;404&quot; data-origin-width=&quot;538&quot; data-origin-height=&quot;575&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;726&quot; data-origin-height=&quot;707&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/STbsx/dJMcagqQjHY/dmNkk4monAZ6H94nskB060/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/STbsx/dJMcagqQjHY/dmNkk4monAZ6H94nskB060/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/STbsx/dJMcagqQjHY/dmNkk4monAZ6H94nskB060/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSTbsx%2FdJMcagqQjHY%2FdmNkk4monAZ6H94nskB060%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;485&quot; height=&quot;472&quot; data-origin-width=&quot;726&quot; data-origin-height=&quot;707&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 오픈미션 1주 차 동안 언어와 주제를 정하고, 새로 추가할 규칙과 기획안을 작성하고 Go 언어 튜토리얼을 읽으면서 오픈미션 제출 때 증빙할 미션 기록을 남기기 위한 템플릿 등을 작성하였다.&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;452&quot; data-origin-height=&quot;105&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c4lgYc/dJMcaaEaYPG/fUo451wTS3KbcWeYVkuokk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c4lgYc/dJMcaaEaYPG/fUo451wTS3KbcWeYVkuokk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c4lgYc/dJMcaaEaYPG/fUo451wTS3KbcWeYVkuokk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc4lgYc%2FdJMcaaEaYPG%2FfUo451wTS3KbcWeYVkuokk%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;452&quot; height=&quot;105&quot; data-origin-width=&quot;452&quot; data-origin-height=&quot;105&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Go 언어의 기초 문법에 익숙해지기 위해 &lt;a href=&quot;https://github.com/Seonwu-K/string-calculator/tree/feat&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;1주 차의 문자열 덧셈기를 Go 언어로 새로 만들어 보기도 하면서 공부&lt;/a&gt;를 했다. 확실히 문서만 읽는 것보다 직접 코드로 구현해 보는 게 이해가 훨씬 잘되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;오픈미션 2-3주 차 - 학습 &amp;amp; 작업&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 2-3주차 동안 출퇴근, 밥 먹는 시간을 제외하고는 Go 언어 학습과 작업에만 모든 시간을 쏟아부었다. 학습을 온전히 다 끝내고 개발에 들어가기에는 시간도 부족했고 어디까지 해야 할지도 알 수 없었기 때문에 정말 기초 문법들만 숙지한 상태로 기능을 개발하면서 기존 학습한 내용 만으로는 불가능하다고 판단되는 문제가 있으면 그 문제를 해결하기 위한 학습을 추가로 하기로 했다.&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;725&quot; data-origin-height=&quot;645&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLDJws/dJMcahQPlSF/DCgn3VXmWWNSz7RiNZ4rUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLDJws/dJMcahQPlSF/DCgn3VXmWWNSz7RiNZ4rUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLDJws/dJMcahQPlSF/DCgn3VXmWWNSz7RiNZ4rUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLDJws%2FdJMcahQPlSF%2FDCgn3VXmWWNSz7RiNZ4rUK%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;403&quot; height=&quot;359&quot; data-origin-width=&quot;725&quot; data-origin-height=&quot;645&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;또한 처음부터 완전한 규칙과 새로운 기능에 대한 틀을 잡기보다는 &lt;b&gt;개발을 해보고 일정보다 구현이 빨리 되면 새로운 규칙이나 기능을 추가하는 방식으로 하나씩 덧붙여 나갔다&lt;/b&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;505&quot; data-origin-height=&quot;627&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjToZt/dJMcafFtAMH/4qiVSHBzxIvXOP71iaQ4nK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjToZt/dJMcafFtAMH/4qiVSHBzxIvXOP71iaQ4nK/img.png&quot; data-alt=&quot;Go 학습 내용 및 생각을 정리한 노트들&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjToZt/dJMcafFtAMH/4qiVSHBzxIvXOP71iaQ4nK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjToZt%2FdJMcafFtAMH%2F4qiVSHBzxIvXOP71iaQ4nK%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;267&quot; height=&quot;332&quot; data-origin-width=&quot;505&quot; data-origin-height=&quot;627&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Go 학습 내용 및 생각을 정리한 노트들&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업하다 보니 시간이 점점 부족해져서 취침 시간도 점점 줄어들고 3~4시간 눈만 붙이고 출근하기도 하는 날이 많아져 점점 지치기도 했지만 이 경험이 싫지는 않았다. 새로운 방식으로 시도하는 학습이 즐겁고 작업에 몰두하다가 잠에 들면서 내일은 뭘 할지 생각하는 시간이 행복했다. &lt;b&gt;작업 시간을 확보하고자 다음 달 연차까지 끌어와서 사용했다..&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;563&quot; data-origin-height=&quot;379&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dtbuyf/dJMcacILxJX/dZrSy0gRFYWp9A24pupIjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dtbuyf/dJMcacILxJX/dZrSy0gRFYWp9A24pupIjK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dtbuyf/dJMcacILxJX/dZrSy0gRFYWp9A24pupIjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdtbuyf%2FdJMcacILxJX%2FdZrSy0gRFYWp9A24pupIjK%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;321&quot; height=&quot;216&quot; data-origin-width=&quot;563&quot; data-origin-height=&quot;379&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;h4 data-ke-size=&quot;size20&quot;&gt;결과물&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 과정을 거쳐 완성한 결과물은 아래 저장소에서 확인 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/Seonwu-K?tab=repositories&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/Seonwu-K?tab=repositories&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1770227605203&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;profile&quot; data-og-title=&quot;Seonwu-K - Overview&quot; data-og-description=&quot;Seonwu-K has 34 repositories available. Follow their code on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/Seonwu-K?tab=repositories&quot; data-og-url=&quot;https://github.com/Seonwu-K&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/DefUN/dJMb8U8OZMz/rMt2pZEPWG4K0C4j48J5kk/img.png?width=185&amp;amp;height=185&amp;amp;face=0_0_185_185,https://scrap.kakaocdn.net/dn/buACMK/dJMb8YXGPp8/mq2AjHi1wu5ICxzIvUADV1/img.png?width=185&amp;amp;height=185&amp;amp;face=0_0_185_185&quot;&gt;&lt;a href=&quot;https://github.com/Seonwu-K?tab=repositories&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/Seonwu-K?tab=repositories&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/DefUN/dJMb8U8OZMz/rMt2pZEPWG4K0C4j48J5kk/img.png?width=185&amp;amp;height=185&amp;amp;face=0_0_185_185,https://scrap.kakaocdn.net/dn/buACMK/dJMb8YXGPp8/mq2AjHi1wu5ICxzIvUADV1/img.png?width=185&amp;amp;height=185&amp;amp;face=0_0_185_185');&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;Seonwu-K - Overview&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Seonwu-K has 34 repositories available. Follow their code 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;추가했던 규칙과 요구사항에 대한 정리는 docs 경로에 문서로 정리하여 첨부해 두었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1058&quot; data-origin-height=&quot;695&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FptZP/dJMcai3doVB/3SXR0wwj2ShW1HZC1Jk830/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FptZP/dJMcai3doVB/3SXR0wwj2ShW1HZC1Jk830/img.png&quot; data-alt=&quot;추가한 확장 규칙&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FptZP/dJMcai3doVB/3SXR0wwj2ShW1HZC1Jk830/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFptZP%2FdJMcai3doVB%2F3SXR0wwj2ShW1HZC1Jk830%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;409&quot; data-origin-width=&quot;1058&quot; data-origin-height=&quot;695&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;추가한 확장 규칙&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;689&quot; data-origin-height=&quot;742&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0bJeL/dJMcadnjPx7/7dpgkLbkeuymL9SyY2ajhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0bJeL/dJMcadnjPx7/7dpgkLbkeuymL9SyY2ajhK/img.png&quot; data-alt=&quot;요구사항 정리&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0bJeL/dJMcadnjPx7/7dpgkLbkeuymL9SyY2ajhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0bJeL%2FdJMcadnjPx7%2F7dpgkLbkeuymL9SyY2ajhK%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;465&quot; height=&quot;501&quot; data-origin-width=&quot;689&quot; data-origin-height=&quot;742&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;요구사항 정리&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 생각한 새로운 규칙이나 기능들을 모두 구현하고 나서 제출까지 시간적 여유가 있었기 때문에 기존 CLI로만 작동하던 프로그램을 웹 브라우저 환경에서도 이용 가능하도록 기능을 추가하고 전체적인 구조를 수정하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;629&quot; data-origin-height=&quot;714&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wNari/dJMcahwxspz/IR2EJJiQHomQpx0OxjF9Q1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wNari/dJMcahwxspz/IR2EJJiQHomQpx0OxjF9Q1/img.png&quot; data-alt=&quot;기존 CLI&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wNari/dJMcahwxspz/IR2EJJiQHomQpx0OxjF9Q1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwNari%2FdJMcahwxspz%2FIR2EJJiQHomQpx0OxjF9Q1%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;416&quot; height=&quot;472&quot; data-origin-width=&quot;629&quot; data-origin-height=&quot;714&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;기존 CLI&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;968&quot; data-origin-height=&quot;833&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cP8kbB/dJMcabJRUdV/N1914JuBKtATEBoIfgfoek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cP8kbB/dJMcabJRUdV/N1914JuBKtATEBoIfgfoek/img.png&quot; data-alt=&quot;추가한 웹 환경에서 멀티 시뮬레이션&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cP8kbB/dJMcabJRUdV/N1914JuBKtATEBoIfgfoek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcP8kbB%2FdJMcabJRUdV%2FN1914JuBKtATEBoIfgfoek%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;499&quot; height=&quot;429&quot; data-origin-width=&quot;968&quot; data-origin-height=&quot;833&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;추가한 웹 환경에서 멀티 시뮬레이션&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;975&quot; data-origin-height=&quot;817&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cXZfQP/dJMcacBZlzr/Gn2V3IceYixL2DGwKrhiO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cXZfQP/dJMcacBZlzr/Gn2V3IceYixL2DGwKrhiO1/img.png&quot; data-alt=&quot;웹 환경에서 시뮬레이션 결과 확인 페이지&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cXZfQP/dJMcacBZlzr/Gn2V3IceYixL2DGwKrhiO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcXZfQP%2FdJMcacBZlzr%2FGn2V3IceYixL2DGwKrhiO1%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;491&quot; height=&quot;411&quot; data-origin-width=&quot;975&quot; data-origin-height=&quot;817&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;웹 환경에서 시뮬레이션 결과 확인 페이지&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 Go 템플릿 코드와 스타일링에는 AI의 도움을 받아서 진행하였다.&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;738&quot; data-origin-height=&quot;442&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBji3X/dJMcab38dS7/Pk6c3C5fdJSG23BZYvRPh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBji3X/dJMcab38dS7/Pk6c3C5fdJSG23BZYvRPh1/img.png&quot; data-alt=&quot;당시 제출했던 문서&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBji3X/dJMcab38dS7/Pk6c3C5fdJSG23BZYvRPh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBji3X%2FdJMcab38dS7%2FPk6c3C5fdJSG23BZYvRPh1%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;536&quot; height=&quot;321&quot; data-origin-width=&quot;738&quot; data-origin-height=&quot;442&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;당시 제출했던 문서&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.2 1차 합격&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1차 합격 공지날이 되고, 오후 3시가 되자 메일 알림이 울렸다. 사실 반쯤 포기하고 있었는데 다행히도 결과는 합격이었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;944&quot; data-origin-height=&quot;663&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/J6GPR/dJMcagYFSQN/vudG5vM3l2JYJzOHPyKmQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/J6GPR/dJMcagYFSQN/vudG5vM3l2JYJzOHPyKmQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/J6GPR/dJMcagYFSQN/vudG5vM3l2JYJzOHPyKmQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJ6GPR%2FdJMcagYFSQN%2FvudG5vM3l2JYJzOHPyKmQK%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;689&quot; height=&quot;484&quot; data-origin-width=&quot;944&quot; data-origin-height=&quot;663&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오픈 미션 결과를 제출하고 나서부터 솔직히 떨어져도 이상할 게 없다고 생각했다. 진행 과정에서 느낀 것도 많고 많이 배우긴 했지만 투자한 시간도 적고 혼자 만들다 보니 다른 참가자들이 공유해 준 오픈미션 결과물과 비교하면 내 작업물은 작고 초라하고 퀄리티가 높은 편도 아니었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진행 과정에 진심으로 임했기 때문에 좋게 봐주신 건지.. 선정 이유는 코치님들만 아실 테니 생각해도 의미가 없었다. 당시 연말이라 바쁜 개인 일정들만 쳐내고 바로 최종 코테를 준비하기로 했다.&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;2. 최종 코딩테스트&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우아한 테크코스의 최종 코딩 테스트는 흔히 기업에서 보는 알고리즘 테스트가 아니라, 프리코스 때 1주일 동안 풀었던 문제들을 5시간 안에 구현해 내는 방식의 테스트이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.1 준비과정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 기수 후기를 보면 최종 코테 준비는 보통 프리코스가 끝나자마자 주기적으로 하시는 분들이 많은 것 같은데 나는 오픈미션이 끝나자마자 오픈미션 동안 미뤄뒀던 개인적인 일들을 처리해야 하기도 했고 솔직히 붙을 거란 생각을 안 했어서 미리 공부해 놓고 떨어지면 너무 억울할 것 같아서 미뤄두고 있었다.   &lt;br /&gt;&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;&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;b&gt;AI 도구는 제한하지만 오픈북&lt;/b&gt;이기 때문에 최대한 많은 자료를 준비하면서 문제를 푸는 연습을 했다. (물론 실전에서 AI를 쓸 수 없기 때문에 구글 검색 옵션이나 코드 에디터의 AI 관련 기능은 모두 비활성화하고 연습했다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 4주 차 문제들이 많이 복잡하고 어려우며 최종 코테는 4주차 문제와 유사하게 나온다고 알고 있었는데, 8기는 정작 4주차 미션이 오픈미션이었기 때문에 아쉬운 대로 이전 기수의 4주차 문제들을 많이 참고했다. &lt;b&gt;5, 6, 7기의 4주차 미션과 최종 코테 문제, 그리고 4기의 최종 코테 문제&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;5시간 내 구현을 위해 집중하는 것부터 어려운 일이었다.&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;평소 코드를 작성할 때 그리고 프리코스 미션에 임할 때에도 시간을 정해두지 않고 여유롭게 코드를 작성하는 편이었는데 5시간 내에 완성하기 위해서는 엄청난 집중력이 필요했다. 그리고 이 준비 과정에서 &lt;b&gt;내가 집중력이 많이 부족해졌음을 체감할 수 있었다.&lt;/b&gt; 처음 문제를 풀어보았을 때 5시간은커녕 &lt;b&gt;기능을 완성했을 때에는 6시간을 훌쩍 넘겼다.&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;나름 단축해 보려고 나만의 snippet도 만들고, 나에게 맞는 시간 분배 루틴도 정해서 처음보다 많이 단축하긴 했지만 &lt;b&gt;마지막 날까지 5시간을 지키지는 못했다&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;채점을 위해 제공되는 테스트 코드가 성공하는 코드를 작성하는 것을 제일 0순위 목표로 둬야 했는데 그걸 너무 늦게 깨달았다.&lt;/b&gt; 시간제한이 있는 문제 풀이를 위한 코드 작성과 완성도 높은 기능을 구현하기 위한 코드 작성은 우선순위와 접근 방식부터가 달랐어야 했는데 세부 요구사항 하나하나에 정신이 팔려서 시간 분배를 제대로 해내지 못했고, 5시간이 다 되어서도 실행이라도 가능한, 전체 코드를 완성시키지 못했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 기수 후기를 찾아보면서 &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;그래서 모든 요구사항을 충족하지는 못하더라도 우선 테스트 코드처럼만이라도 돌아가는 코드를 만드는 것을 목표로 연습을 이어갔고, 시험 이틀 전 즈음에는 5시간 언저리까지 시간을 단축하긴 했지만 합격을 위해서는 코드를 리팩토링 할 시간도 있어야 하고, 테스트 코드도 추가해야 할 텐데.. 그리고 제출할 때 소감문도 작성해야 할 텐데 구현만으로 이렇게 시간이 걸린다면 합격은 불가능하지 않을까 하는 생각뿐이었다. 합격자들의 후기를 보면 1~2시간 남겨두고 구현을 끝내고 리팩토링도 하고 소감문도 미리 써두는 듯했는데 기능 구현만 해도 5시간이 걸리는 듯했다.&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;441&quot; data-origin-height=&quot;209&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6Hrvj/dJMcacorzsz/F5bht85xOQks4B22RXR5Kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6Hrvj/dJMcacorzsz/F5bht85xOQks4B22RXR5Kk/img.png&quot; data-alt=&quot;전날과 전전날 기록한 뽀모도로 (4시간 30분이라고 써있긴 하지만 5시간 내로는 불가능함을 느끼고 4시간 반부터 타이머를 끄고 작업을 했다. 실제로는 5시간 반정도 걸렸다.)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6Hrvj/dJMcacorzsz/F5bht85xOQks4B22RXR5Kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6Hrvj%2FdJMcacorzsz%2FF5bht85xOQks4B22RXR5Kk%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;441&quot; height=&quot;209&quot; data-origin-width=&quot;441&quot; data-origin-height=&quot;209&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;전날과 전전날 기록한 뽀모도로 (4시간 30분이라고 써있긴 하지만 5시간 내로는 불가능함을 느끼고 4시간 반부터 타이머를 끄고 작업을 했다. 실제로는 5시간 반정도 걸렸다.)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좀 더 일찍 시험을 준비해두지 못해 아쉽다는 마음을 뒤로한 채 마지막 날에도 아슬아슬하게 시간을 초과하면서 마지막 문제를 겨우 풀어내고 최종 문제가 쉽기를 기도하며 잠에 들었다.&lt;/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;2.2 최종 코테 후기&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코딩 테스트 당일날 아침 간단한 아침을 먹고 일찍 도착했다. 11시에 도착을 했는데 12시부터 입장이 가능하기 때문에 1층 카페에서 커피를 마시면서 그동안 풀었던 코드들을 다시 읽었고, 45분쯤 올라가서 줄을 서있다가 12시가 되자마자 입장했다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;새로운 굿즈?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부 풍경을 지나쳐 가장 안쪽 시험장소의 내 자리에 가서야 굿즈를 보고 카메라를 꺼내 들었는데, 기존에 나눠주던 굿즈와는 달리 민트색 쇼핑백이 각자의 자리에 올려져 있었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;3024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Jqf6B/dJMcai3dpK2/4JcKGkGhhnNfN1ZqvwSh71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Jqf6B/dJMcai3dpK2/4JcKGkGhhnNfN1ZqvwSh71/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Jqf6B/dJMcai3dpK2/4JcKGkGhhnNfN1ZqvwSh71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJqf6B%2FdJMcai3dpK2%2F4JcKGkGhhnNfN1ZqvwSh71%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;480&quot; height=&quot;480&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나중에 집에 와서 열어보니 &lt;span style=&quot;color: #0593d3;&quot;&gt;때밀이 수건과 민트 향이 나는 비누&lt;/span&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;459&quot; data-origin-height=&quot;392&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mYhdu/dJMcaaxpCwS/sQwgm3vCpP7j9nFd7C0MF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mYhdu/dJMcaaxpCwS/sQwgm3vCpP7j9nFd7C0MF1/img.png&quot; data-alt=&quot;현재는 판매하지 않는 때수건&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mYhdu/dJMcaaxpCwS/sQwgm3vCpP7j9nFd7C0MF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmYhdu%2FdJMcaaxpCwS%2FsQwgm3vCpP7j9nFd7C0MF1%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;236&quot; height=&quot;202&quot; data-origin-width=&quot;459&quot; data-origin-height=&quot;392&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;현재는 판매하지 않는 때수건&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나름 민트향도 좋고 보기에도 예뻐서 화장실에 데코용으로 비치해 뒀다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시험 시작&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메일에 적혀 있던 안내사항 대로 키보드와 마우스는 사용 가능해서 안내받은 자리에서 키보드, 거치대, 마우스를 설치했다. 그렇게 1시가 되고 미션이 공개가 되었는데.. &lt;b&gt;이번 코딩 테스트는 4주 차 미션과 마찬가지로 기존과는 전혀 다른 유형의 문제가 출제되었다.&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;626&quot; data-origin-height=&quot;182&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/42zp6/dJMcad1Ujrm/l6ZAAFg4X5Vmz4TWsvhYDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/42zp6/dJMcad1Ujrm/l6ZAAFg4X5Vmz4TWsvhYDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/42zp6/dJMcad1Ujrm/l6ZAAFg4X5Vmz4TWsvhYDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F42zp6%2FdJMcad1Ujrm%2Fl6ZAAFg4X5Vmz4TWsvhYDk%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;478&quot; height=&quot;139&quot; data-origin-width=&quot;626&quot; data-origin-height=&quot;182&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제 유형뿐 아니라 시간 구성부터 바뀌었는데 기존에는 13시부터 18시까지 5시간이 주어졌지만, 이번에는 17시까지로 4시간이 주어졌다. 그 이후에는 추가 푸시를 허용하지 않으며, 1시간 동안 회고를 작성하여 제출해야 했다. 가뜩이나 5시간 내에 완성한 적도 없는데 4시간이라니 할 수 있을까 걱정이 되기도 했다가 1시간을 줄였다는 건 그만큼 문제 난이도를 낮췄다는 뜻이 아닐까 하는 기대도 했다. 또한 1시간을 온전히 회고에만 집중하는 시간이 있다는 것은 &lt;b&gt;문제 풀이만큼이나 소감문 내용도 평가에서 비중이 크겠다는 생각이 들었다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;시험 문제&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시험 문제가 공개되고 &lt;b&gt;예상과 너무 달라서 놀랐다&lt;/b&gt;. 기존 코테 문제들은 프리코스 미션들과 형식이 유사하긴 해도 아예 다른 내용으로 출제가 되었다. 그 난이도는 기수마다 차이는 있지만 규칙 자체는 미션보다 조금 더 복잡해도 요구사항 자체는 5시간 내에 구현할 수 있도록 줄여두었구나 싶은 정도였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 8기의 코딩 테스트 문제는 &lt;b&gt;8기 3주 차의 미션과 완전히 동일한 '로또 미션'&lt;/b&gt;이 그대로 나왔다. 아니 오히려 기존 과제에서 구현했던 &lt;b&gt;로또 결과를 통계로 출력하는 부분을 구현하지 않아도 되는&lt;/b&gt; 오히려 &lt;b&gt;적은 분량의 문제&lt;/b&gt;가 나왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 출력을 위한 &lt;b&gt;OutputView 클래스를 미리 주어주고 이 코드를 수정하지 않은 채 그대로 사용하여 입출력 요구사항을 지키도록 코드를 완성&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;/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;누가 봐도 지금까지 출제된 모든 코딩 테스트 문제 중 가장 쉬운 문제였다. 시간을 줄인 데에는 이유가 있구나 생각했다만 이번 8기 시험은 또 한 가지 다른 점이 있었다.&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;812&quot; data-origin-height=&quot;628&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjcXXh/dJMcab38fhl/4Ub8DwJUYOclxBLFshbGzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjcXXh/dJMcab38fhl/4Ub8DwJUYOclxBLFshbGzK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjcXXh/dJMcab38fhl/4Ub8DwJUYOclxBLFshbGzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjcXXh%2FdJMcab38fhl%2F4Ub8DwJUYOclxBLFshbGzK%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;502&quot; height=&quot;388&quot; data-origin-width=&quot;812&quot; data-origin-height=&quot;628&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;p data-ke-size=&quot;size16&quot;&gt;요구사항에 대한 기능 구현을 모두 끝내면 &lt;b&gt;추가로 도전 과제를 진행&lt;/b&gt;해야 했다. 하나를 선택하여 도전하라고 나와있지만 사실하다 보면 둘 다 하게 될 것 같았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;그렇게 코드를 클론 해서 리포지토리 설정을 하고 PR을 미리 만들어 두고 테스트 코드를 확인했는데, &lt;b&gt;이번 문제는&lt;/b&gt; &lt;b&gt;테스트 케이스도 두 가지밖에 없었다&lt;/b&gt;. 기존 문제들은 적어도 4~5개는 되었는데 이 정도로 줄어들었다면 테스트 케이스를 통한 채점은 사실상 별 의미가 없어졌고 &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;기능 구현은 생각보다 빠르게 완성되었다. 최종 코테 준비 기간 동안 로또 미션을 다시 본 적은 없지만 오픈 미션 자체를 나는 로또 미션을 Go로 재구현하고 기능도 추가하는 과정에서 수도 없이 다시 읽어보았기 때문에 걱정과는 달리 중간중간 커밋도 찍고 README도 수정하면서 &lt;b&gt;2시간 만에 기능을 완성&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;도전 과제는 우선 가볍게 리팩토링을 진행하였다. 구조 자체는 기존의 미션과 비슷하게 완성을 했었기 때문에 기존 미션에서 받았던 피드백도 다시 찾아서 읽고 적용해 보고 개인적으로 아쉽게 생각했던 패키지 구조를 수정하거나 enum으로 분리 가능한 하드코딩 값들을 따로 분리하는 식으로 리팩토링 해주었다.&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;오픈 미션에서 이미 실제 로또에 가깝도록 만드는 규칙이나 추가 기능은 하도 많이 확장해 봐서 도저히 오픈 미션과 겹치지 않는 추가 기능이 떠오르지 않았다&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;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;. 아차 싶었다. 기존 테스트 코드는 입력이 정해져 있었고 거기에 추가 입력을 받아야 동작하는 방식으로 기능을 구현했으니 당연히 입력 값이 부족하여 테스트가 깨질 수밖에 없었다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1770234725100&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    @Test
    void 기능_테스트() {
        assertRandomUniqueNumbersInRangeTest(
            () -&amp;gt; {
                run(&quot;1000&quot;, &quot;1,2,3,4,5&quot;, &quot;6&quot;);
                assertThat(output()).contains(
                        &quot;2개를 구매했습니다.&quot;,
                        ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하려면 run 메서드에 넘기는 입력 문자열에 커맨드 값을 추가해야 했는데 그러면 기존 테스트 코드를 수정하게 된다. 기능 추가는 기존 테스트 코드를 수정하지 않고 통과하도록 작성해야 했기 때문에 입력을 받지 않고 작동할 수 있도록 기능을 만들어야 했다. 하지만 제출까지 시간이 얼마 남지 않아서 기능에 사용된 클래스를 모두 수정하기엔 시간이 부족했다. &lt;b&gt;아쉽지만 추가 기능을 사용하는 코드를 컨트롤러에서 주석처리를 하고 다시 정상적으로 테스트 코드가 동작하는 것을 확인한 후 제출&lt;/b&gt;을 할 수 밖에 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;회고 작성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회고에서는 지금까지 어떻게 프리코스에 임했고, 최종 시험은 어떻게 준비했고, 그 과정에서 무엇을 얻었는지를 적은 뒤 이번 시험에 대해 내가 어떤 의도로 설계를 했는지, 도전 과제에 대한 설명과 주석 처리를 한 상태로 제출하게 된 이유 등 시험을 진행한 과정을 풀어서 적고 가벼운 후기로 마무리했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회고의 비중이 매우 커졌음을 인지하긴 했지만 내가 글솜씨가 좋지 않은 편이라 괜히 욕심을 부려서 많은 얘기를 적어봤자 읽기 어렵기만 할 것이라고 생각했다. 결국 &lt;b&gt;이 글을 읽고 평가하는 사람이 읽기 쉬우면서도 와닿을 수 있는 글을 써야 한다고 생각했다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 &lt;b&gt;꼭 전하고 싶은 심정과 우테코 과정에서 어떻게 생각했고 무엇을 느꼈는지&lt;/b&gt; 정도만 떠오르는 대로 적고 두 번 정도 다시 읽어보면서 읽기 쉽게 다듬고 마무리했다. 시간이 30분 정도 남아 있었지만 &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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;778&quot; data-origin-height=&quot;608&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4B3jV/dJMcaac6DYo/gHejyzTaTxLHG8AKFm0Nc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4B3jV/dJMcaac6DYo/gHejyzTaTxLHG8AKFm0Nc0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4B3jV/dJMcaac6DYo/gHejyzTaTxLHG8AKFm0Nc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4B3jV%2FdJMcaac6DYo%2FgHejyzTaTxLHG8AKFm0Nc0%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;563&quot; height=&quot;440&quot; data-origin-width=&quot;778&quot; data-origin-height=&quot;608&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.3 8기 최종 합격  &lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;합격 발표가 있던 날, 밤잠을 설쳤다.. 아침 일찍 발표도 아니고 오후 3시 발표지만 도저히 잠자리에 누워도 잠이 안 왔다. 자려고 시도하다 잠이 안 와서 폰을 꺼내서 유튜브를 보고 다시 덮고 눈을 감고를 반복하다 아침이 되어서야 겨우 잠이 들긴 했는데 핸드폰 알림 소리 하나하나에 반응하며 계속 잠이 깨고 시계를 보고 아직이구나 하고 다시 잠에 들고를 반복하다가 2시 50분쯤 알람이 울렸다. 다시 시계를 보고 아직 3시 전이네 하고 다시 자려다가 방금 울린 알람이 네이버 알람이길래 화들짝 놀라서 메일함을 열어봤다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;633&quot; data-origin-height=&quot;483&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YfJvj/dJMcaivoxKh/Kj67tV2hlXk6NLgnTjJVV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YfJvj/dJMcaivoxKh/Kj67tV2hlXk6NLgnTjJVV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YfJvj/dJMcaivoxKh/Kj67tV2hlXk6NLgnTjJVV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYfJvj%2FdJMcaivoxKh%2FKj67tV2hlXk6NLgnTjJVV0%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;583&quot; height=&quot;409&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;633&quot; data-origin-height=&quot;483&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;p data-ke-size=&quot;size16&quot;&gt;최선을 다했고 결과가 어찌 됐든 나는 많은 걸 배웠다고 되새겼지만 그럼에도 본 과정까지 가고 싶다는 마음도 너무 커서 합격 발표전날까지 계속 긴장하고 예민한 상태로 지내왔었다. 감사하게도 연초부터 큰 선물을 받게 되었고 지금까지 노력해 온 시간이 떠올라 정말 뿌듯했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아 그리고 나중에 알게 된 사실인데 메일을 지메일로 입력한 사람들은 3시가 넘어서도 메일이 바로 오지 않았다고 한다. 3시 이전에 메일을 받지 못했다면 나도 떨어졌을 거라고 생각하며 우울하게 있었을 걸 생각하면.. 메일을 네이버로 적어서 정말 다행이다..&amp;nbsp; 네이버 짱짱   앞으로도 결과 통보받을 메일은 네이버로 적어야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&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;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2025년을 불안에 떨며 보냈었는데 2026년의 시작은 더할 나위 없이 좋은 스타트라 정말 다행이다. 내가 부족하다는 것을 느낄 때마다 자존감도 많이 떨어지고 스트레스도 많이 받았지만 결국 노력한 만큼 만족스러운 결과를 맞이할 수 있었다.&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;h4 data-ke-size=&quot;size20&quot;&gt;여담으로...(feat. 블로그, 깃허브 닉네임을 바꾼 이유..)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1월 말 사전 준비 안내 메일을 통해 준비해야 할 것들을 전달받았는데 우테코 활동 닉네임을 정해야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 내가 기존에 사용하던 닉네임들이 찾아보니 이전 기수 참가자들이 모두 사용했던 닉네임이었다  (특히 UMC부터 사용해 온 머랭..)&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;그렇게 이번 8기에서 '루디'라는 이름으로 활동하게 되었다. 이번에 정한 닉네임은 앞으로도 바꾸지 않겠다는 다짐으로 블로그명과 깃허브 이름 모두 수정 완료  &lt;/p&gt;</description>
      <category>My Story/우아한테크코스</category>
      <category>우아한테크코스</category>
      <category>우테코 백엔드</category>
      <category>우테코 최종 코테</category>
      <category>최종합격</category>
      <author>_루디_</author>
      <guid isPermaLink="true">https://kimjoraeng.tistory.com/87</guid>
      <comments>https://kimjoraeng.tistory.com/87#entry87comment</comments>
      <pubDate>Thu, 5 Feb 2026 05:53:10 +0900</pubDate>
    </item>
    <item>
      <title>[회고] 우아한 테크코스 백엔드 8기 프리코스 3주차 - 로또 시뮬레이션</title>
      <link>https://kimjoraeng.tistory.com/86</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번 주차는 '로또' 시스템을 구현하는 미션이 제시되었다.&lt;br /&gt;문제 자체는 간단하지만 1,2주 차 동안 누적된 공통 피드백과 학습목표, 지난 미션동안 리뷰로 받은 피드백 등을 의식하면서 설계하고 구현하는 것이 관건이었다.&lt;br /&gt;그리고 개인적으로는 이번 과제에서 Enum Type에 대한 활용과 에러 메시지에 대한 포맷팅, 잘못된 입력에 대한 반복 입력 등을 구현해야 하고 예외를 던질 때도 예외의 의미를 한번 더 곱씹는 과정에서 생각할 것이 정말 많다고 느껴지면서도 덕분에 많은 내용을 배울 수 있었다고 생각한다.&lt;br /&gt;이번 미션 동안 고민했던 점, 새롭게 적용했던 점, 아쉬웠던 점, 받은 피드백 등을 정리해보았다.  &lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;다운로드.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;473&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wuyqY/dJMcaiBDJjs/Luk4HQrUkN1afdE1mm27lK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wuyqY/dJMcaiBDJjs/Luk4HQrUkN1afdE1mm27lK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wuyqY/dJMcaiBDJjs/Luk4HQrUkN1afdE1mm27lK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwuyqY%2FdJMcaiBDJjs%2FLuk4HQrUkN1afdE1mm27lK%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;1280&quot; height=&quot;473&quot; data-filename=&quot;다운로드.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;473&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;⚡구현 과정 요약&lt;/h2&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;사용자가 입력한 금액만큼 로또를 발행(1000원 단위, 6자리 중복없는 랜덤번호)&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;로또 번호의 숫자 범위는 1~45까지&lt;/li&gt;
&lt;li&gt;중복되지 않는 6개의 숫자와 보너스 번호 1개를 저장한다.&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;/ul&gt;
&lt;/li&gt;
&lt;li&gt;에외 상황이 발생하면 &lt;code&gt;[ERROR]&lt;/code&gt;로 시작하는 에러 메시지를 출력한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️ 시스템 다이어그램&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 주차에 설계에 많은 시간을 쏟았으나 구현 시간이 부족했던 점에서 아쉬움을 느꼈기 때문에, 이번에는 큰 단위의 클래스로 역할을 분담하고 구체적인 의존 관계나 필요한 메서드 등은 구현하면서 점차 추가하는 방식으로 접근 방법을 바꿔보았다. 실제로 작업 도중 수정 이 필요한 내용은 계속 발생했지만 수정을 염두에 두고 설계를 디테일하게 잡아두지 않았기에 빠른 수정이 가능했고, 수정을 반복해도 부담이 되지 않았기 때문에 지금의 방법이 현재로서는 가장 잘 맞는 방법인 것 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1812&quot; data-origin-height=&quot;1071&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpeRgN/dJMcadtyU8f/4Kjm61Yb07o2L2ujlQwdhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpeRgN/dJMcadtyU8f/4Kjm61Yb07o2L2ujlQwdhK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpeRgN/dJMcadtyU8f/4Kjm61Yb07o2L2ujlQwdhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcpeRgN%2FdJMcadtyU8f%2F4Kjm61Yb07o2L2ujlQwdhK%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;1812&quot; height=&quot;1071&quot; data-origin-width=&quot;1812&quot; data-origin-height=&quot;1071&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  패키지 구조&lt;/h3&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;  lotto
├─ .gitignore
├─ README.md
├─ build.gradle
├─ settings.gradle
├─ gradlew
├─ gradlew.bat
├─ gradle
│  └─ wrapper
│     ├─ gradle-wrapper.jar
│     └─ gradle-wrapper.properties
└─ src
   └─ main
      └─ java
         └─ lotto
            ├─ Application.java
            ├─ config
            │  └─ AppConfig.java
            ├─ controller
            │  └─ LottoController.java
            ├─ global
            │  ├─ constants
            │  │  └─ NumberType.java
            │  └─ exception
            │     └─ ErrorMessage.java
            ├─ model
            │  ├─ BonusNumber.java
            │  ├─ Lotto.java
            │  ├─ Lottos.java
            │  ├─ LottosFactory.java
            │  ├─ Rankings.java
            │  ├─ WinningNumbers.java
            │  └─ WinningResult.java
            └─ view
               ├─ InputStringParser.java
               ├─ InputView.java
               ├─ LottoFormatter.java
               ├─ OutputView.java
               ├─ RankingLabelFormatter.java
               └─ ResultFormatter.java&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️구현 기능 목록&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;로또 구입 금액의 입력에 맞게 로또 발행&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 구매 금액대로 로또 랜덤 생성하여 저장&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 생성된 로또들 콜렉션으로 관리당첨 번호와 보너스 번호 입력&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 안내멘트 출력 후 입력받기(금액, 당첨번호, 보너스번호)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 공통 - 빈 문자열인지 검사&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 공통 - 숫자 입력이 맞는지 검사&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 금액 - 1000원으로 나누어 떨어지는지 검사 후 파싱&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 당첨번호 - 구분자가 정상적인지 검사 -&amp;gt; 구분자 기준으로 분리&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 당첨번호/보너스번호 - 범위 내의 값인지 검사(1~45)&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 당첨번호/보너스번호 - 중복되지 않았는지 검사&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 당첨번호/보너스번호 - 파싱&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 로또 당첨번호 저장&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 로또 보너스 번호 저장&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 로또 등수 조건 정의&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 랜덤생성 로또와 당첨번호&amp;amp;보너스번호 대조하여 등수 결과 카운트(총합 계산) 당첨 결과와 수익률 출력&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 랜덤생성된 로또 결과 출력(총 수량, 각 번호)&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 당첨 결과 출력&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 총 수익률 출력&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  이번 주차에 고민한 내용(feat. PR 리뷰)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;구입금액을 입력해 주세요.  
8000  

8개를 구매했습니다.  
[8, 21, 23, 41, 42, 43]  
[3, 5, 11, 16, 32, 38]  
[7, 11, 16, 35, 36, 44]  
[1, 8, 11, 31, 41, 42]  
[13, 14, 16, 38, 42, 45]  
[7, 11, 30, 40, 42, 43]  
[2, 13, 22, 32, 38, 45]  
[1, 3, 5, 14, 22, 45]  

당첨 번호를 입력해 주세요.
1,2,3,4,5,6  

보너스 번호를 입력해 주세요.  
7  

당첨 통계  
---  
3개 일치 (5,000원) - 1개  
4개 일치 (50,000) - 0개  
5개 일치 (1,500,000원) - 0개  
5개 일치, 보너스 불 일치 (30,000,000원) - 0개  
6개 일치 (2,000,000,000원) - 0개  
총 수익률은 62.5%입니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에러 메시지에 대한 처리에서도 &lt;code&gt;[ERROR]&lt;/code&gt; 라는 태그를 앞에 붙이고 메시지를 던질 때 무엇으로 인해 에러가 발생하는지 함께 출력하기도 하려면 문자열과 값을 합쳐 보기 좋게 포맷팅하는 과정이 필요하다고 판단했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지는 문자열에 + 연산자로 숫자 데이터를 이어붙여서 출력하는 방식을 사용했었는데, 자바에도 이런 포맷을 지원하는 API가 있을 것이라 생각하고 찾아보았다. &lt;a href=&quot;https://code-boki.tistory.com/209&quot;&gt;이 포스팅&lt;/a&gt;을 참고하여 적용하였으며 간단히 정리하면 다음과 같다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✏️ String 더하기&lt;br /&gt;지금까지 사용해왔던, 가장 단순하고 강력한 방법이다.&lt;br /&gt;문자열과 값이 번갈아서 나오는 경우 번거롭게 느껴진다.&lt;br /&gt;ex) System.out.println(&quot;이름: &quot; + name + &quot;, 나이: &quot; + age)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✏️ printf&lt;br /&gt;C언어를 해봤으면 익숙할 그 printf와 사용법이 같다.&lt;br /&gt;ex) System.out.printf(&quot;이름: %s, 나이 : %d\n&quot;, name, age);&lt;br /&gt;사용할 데이터에 따른 포맷형식을 인지하고 있어야 하고, 메모리 관련 포맷 형식을 사용하면 UnknownForamtConversion 예외 발생&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✏️ StringBuilder&lt;br /&gt;자바 코테 책이나 풀이에서 자주 소개되며, 자동으로 String으로 포맷을 다 변환해 주기 때문에 %d, %f 같은 형식 없이 사용 가능&lt;br /&gt;ex)&lt;br /&gt;StringBuilder sb = new StringBuilder()&lt;br /&gt;sb.append(&quot;이름: &quot;).append(name).append(&quot;, &quot;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러줄로 표현해야 하기 때문에 가독성 면에서 아쉬운 선택지다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✏️ String.format()&lt;br /&gt;printf와 유사하게 형식을 지정할 수 있다. 차이점은 printf는 포맷팅 하면서 바로 출력한다면 String.format은 String으로 반환하여 재사용하거나 더 가공할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✏️ MessageFormat.format()&lt;br /&gt;placeholder를 지원하여 위치 기반으로 변수를 입력할 수 있다. 객체 기반 포맷팅을 지원하여 배열이나 리스트 등의 객체에서 값을 쉽게 삽입할 수도 있다.&lt;br /&gt;ex) String str = MessageFormat.format(&quot;이름: {0}, 키: {1}&quot;, name, age)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전반적으로 format 메서드를 쓰는 방식이 가장 깔끔하고 개인적으로 MessageFormat이 더 가독성이 좋아보였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로, 숫자에 대한 출력을 할 때 두 format 방식 모두 천 단위 구분자를 찍는 기능을 지원했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;MessageFormat.format(&quot;{0, number}&quot;, 100000)&lt;/code&gt; 이렇게 값을 넣어주면 &lt;code&gt;100,000&lt;/code&gt; 형태로 문자열을 만들어 준다.(다만 로케일이 dp_DE면 온점으로 찍히니 확인 필요)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;정적 팩토리 메소드 적용해보기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 주차에는 정적 팩토리 메소드를 적용해보기로 했다. &lt;a href=&quot;https://kimjoraeng.tistory.com/80#%EB%8B%A4%EC%9D%8C%20%EC%A3%BC%EC%B0%A8%20%EB%AA%A9%ED%91%9C-1&quot;&gt;1주차 회고&lt;/a&gt;를 작성할 때 다음 목표로 정적 팩토리를 언급했는데, 2주차에 다른 요소들을 적용하다 고려하지 못하고 지나쳐버렸다.. 이번에야말로 적용을 해보기로 했다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;정적 팩토리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 기존 new 키워드와 생성자를 통한 인스턴스 생성 방식은 이름이 클래스명으로 고정되어 있기 때문에, 일급 객체나 일급 컬렉션을 다룰 때 어떤 목적으로 생성되는 데이터인지 나타내는 것이 어려웠다.&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 생성 자체를 숨기고 생성 하는 책임을 한 곳으로 모아두는 것이 가능해서 생성에 대하여 변경이 발생하더라도 변경을 최소화할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 로또 과제에서 당첨번호와 보너스 번호를 다루는 WinningNumbers는 생성 시점에 당첨 번호와 보너스 번호가 중복되지 않아야 한다는 도메인 규칙이 존재한다. 생성 시점에서 검증이 필요했고, 이외의 생성 경로를 차단할 필요가 있었다. 생성자 자체를 private으로 막고, 정적 팩토리 메서드를 통해 접근 가능하도록 하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;218&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNG7aZ/dJMcafx87fj/Laq0ClwEUsbsikKJLAOb70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNG7aZ/dJMcafx87fj/Laq0ClwEUsbsikKJLAOb70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNG7aZ/dJMcafx87fj/Laq0ClwEUsbsikKJLAOb70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNG7aZ%2FdJMcafx87fj%2FLaq0ClwEUsbsikKJLAOb70%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;682&quot; height=&quot;218&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;218&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;of라는 키워드를 통해 입력 값 기반으로 WinningNumbers를 생성한다는 의미를 담아서 당첨번호와 보너스번호를 생성하도록 하였다. of를 메서드명으로 설정하여 어떤 데이터로부터 생성되는 클래스인지를 보여주는 효과도 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;자바의 복사 방식(feat. 안전한 getter)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1주 차부터 getter에 대해 신경 쓰다가 2주 차에서는 getter를 지양하고자 직접 자신의 값을 외부로 내주는 게 아니라 객체 측에서 자신의 정보를 담아서 출력을 처리하도록 하는 과정에서 도메인에서 출력의 책임을 가지게 하는 실수를 했었다.&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;기본적으로 원시값은 불변이기 때문에 그대로 get을 해도 문제가 없으며, 문제는 참조 변수 그중에서도 자주 쓰이는 컬렉션의 처리가 문제였다. 참조변수가 반환된 곳에서 수정되면 원본에도 영향이 가는 것이 문제이기 때문에 불변으로 반환하는 방법에 대해 숙지해야 했다. 자바의 복사 방식에 대해 다루는 &lt;a href=&quot;https://ksh-coding.tistory.com/77&quot;&gt;이 포스팅&lt;/a&gt;을 참고하였다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  1. 방어적 복사&lt;br /&gt;내부의 객체를 반환할 때, 객체의 복사본을 만들어서 반환하는 것을 방어적 복사라고 한다.&lt;br /&gt;방어적 복사를 하게 되면, 복사한 외부의 객체를 변경해도 원본 내부 객체가 변경되지 않는다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  2. 얕은 복사&lt;br /&gt;원본 객체를 복사할 때, 새로운 객체를 만들지만 원본 객체의 '주소 값'을 참조하는 복사이다.&lt;br /&gt;따라서 원본이나 복사한 객체나 변경이 되면 서로 영향을 미친다.&lt;br /&gt;(call-by-reference와 유사한 개념)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  3. 깊은 복사&lt;br /&gt;원본 객체를 복사할 때, 새로운 객체를 만들고 원본 객체의 모든 값을 복사해서&lt;br /&gt;원본 객체로부터 독립적인 객체를 생성한다.&lt;br /&gt;따라서 원본과 복사한 객체가 독립적이므로 변경이 되어도 서로 영향을 미치지 않는다.&lt;br /&gt;(call-by-values와 유사한 개념)&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. 내부에서 반환한 리스트를 변경해도 된다(해야 한다).&lt;br /&gt;  new ArrayList &amp;lt;&amp;gt;() 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 내부에서 반환한 리스트를 변경하면 안 되고, 원본 리스트가 변경될 일이 없다.&lt;br /&gt;(변경될 때 복사한 리스트에 영향을 줘도 된다.)&lt;br /&gt;  Collections.unmodifiableList() 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 내부에서 반환한 리스트를 변경하면 안 되고, 원본 리스트 변경 시 복사한 리스트에 영향을 주면 안 된다.&lt;br /&gt;  List.copyOf() 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 내부에서 반환한 리스트 안의 객체 요소까지 내부 원본 객체에 영향을 주지 않아야 한다.&lt;br /&gt;  복사 생성자 + unmodifiableList() 사용&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 기준에 따라 과제에서는 컬렉션 값을 생성하거나, 반환하는 과정에서 new ArrayList &amp;lt;&amp;gt;와 copyOf를 적극적으로 사용하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;442&quot; data-origin-height=&quot;152&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cU7Ooq/dJMcaksGET6/NBnAcmHnmo8yKMd2Jdd2wk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cU7Ooq/dJMcaksGET6/NBnAcmHnmo8yKMd2Jdd2wk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cU7Ooq/dJMcaksGET6/NBnAcmHnmo8yKMd2Jdd2wk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcU7Ooq%2FdJMcaksGET6%2FNBnAcmHnmo8yKMd2Jdd2wk%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;442&quot; height=&quot;152&quot; data-origin-width=&quot;442&quot; data-origin-height=&quot;152&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Lottos는 여러 Lotto값을 보관하는 일급 컬렉션이기 때문에 생성 시 입력받은 컬렉션을 그대로 참조하면 외부에서 리스트를 수정할 경우 내부 상태에 변경 가능성이 생긴다. 이를 방지하기 위해 new ArrayList &amp;lt;&amp;gt;(lottos)를 사용하여 방어적 복사를 적용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 Lottos 내부의 리스트는 도메인 핵심이라 수정이 되면 안 되었다. 하지만 외부에서도 값을 활용해야 했기 때문에 안전하게 반환하기 위해 불변 상태로 복사해서 반환하는 copyOf를 사용해 주었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;406&quot; data-origin-height=&quot;160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mmueb/dJMcabJhvuf/CgFfqplpVrGCKA1w1W5SQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mmueb/dJMcabJhvuf/CgFfqplpVrGCKA1w1W5SQk/img.png&quot; data-alt=&quot;이번 주차에서 칭찬 리뷰도 받았다  &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mmueb/dJMcabJhvuf/CgFfqplpVrGCKA1w1W5SQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fmmueb%2FdJMcabJhvuf%2FCgFfqplpVrGCKA1w1W5SQk%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;406&quot; height=&quot;160&quot; data-origin-width=&quot;406&quot; data-origin-height=&quot;160&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이번 주차에서 칭찬 리뷰도 받았다  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;마지막줄 개행&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;398&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kxcip/dJMcai2HXwx/nxg6DuVQwKmOomHxXxOtjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kxcip/dJMcai2HXwx/nxg6DuVQwKmOomHxXxOtjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kxcip/dJMcai2HXwx/nxg6DuVQwKmOomHxXxOtjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fkxcip%2FdJMcai2HXwx%2Fnxg6DuVQwKmOomHxXxOtjk%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;501&quot; height=&quot;277&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;398&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@doondoony/posix-eol&quot;&gt;파일 끝에는 항상 개행을 추가해야 해요&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 내용은 2주 차 당시 내가 마지막 줄에 개행을 없애서 받았던 리뷰 내용이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막에 개행을 추가하는 것이 의도적인 것인 줄도 모르고 내가 언제 넣었지 하고 직접 지우고 있었다  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;찾아보니 파일 마지막 줄에 개행을 추가하는 규칙은 POSIX 표준에서 비롯된 것으로, 텍스트 파일은 마지막 줄이 개행 문자로 끝나야 완전한 한 줄로 인식되는 것 같다. 이게 없으면 Git diff나 다른 텍스트 도구들이 끝나지 않은 상태로 처리하여 저렇게 경고를 발생하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 추가하기 위한 설정을 하기도 하는 것 같은데 내가 첫 주차에 설정했던 스타일 포맷에 이미 포함된 부분이라 직접 지우는 일만 없으면 될 것 같다..&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;static 올바르게 사용하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1주 차에 static을 사용에 대한 피드백을 받은 적이 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;711&quot; data-origin-height=&quot;167&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brgsDx/dJMcai9tAnT/dK99bglKg7sAvBuPEYWSw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brgsDx/dJMcai9tAnT/dK99bglKg7sAvBuPEYWSw0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brgsDx/dJMcai9tAnT/dK99bglKg7sAvBuPEYWSw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrgsDx%2FdJMcai9tAnT%2FdK99bglKg7sAvBuPEYWSw0%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;596&quot; height=&quot;140&quot; data-origin-width=&quot;711&quot; data-origin-height=&quot;167&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;659&quot; data-origin-height=&quot;132&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lldRl/dJMcaboYEsN/cPkEO48hJJydbwoLkf3xW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lldRl/dJMcaboYEsN/cPkEO48hJJydbwoLkf3xW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lldRl/dJMcaboYEsN/cPkEO48hJJydbwoLkf3xW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlldRl%2FdJMcaboYEsN%2FcPkEO48hJJydbwoLkf3xW1%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;659&quot; height=&quot;132&quot; data-origin-width=&quot;659&quot; data-origin-height=&quot;132&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로, 해당 클래스는 상태를 가지고 있지 않은 순수 유틸 함수이기 때문에 static으로 사용한다고 객체지향을 벗어났다고는 할 수 없으나 당시 도메인과 계층에 대한 이해 없이 validation 하는 코드들을 모아놓고 관리하려는 목적으로 사용했기 때문에 SRP에서는 벗어난 코드가 맞았다. 하지만 당시 나는 static은 객체지향을 벗어난 문법인가?라는 생각에 &lt;b&gt;static 사용이 문제라고&lt;/b&gt; &lt;b&gt;생각&lt;/b&gt;해서 static을 최대한 지양하면서 2주 차 코드를 작성했다. 그 결과 써야 할 곳에서도 제대로 사용하지 못하는 상황도 발생했다. (상수를 따로 처리하지 않고 모든 값을 하드코딩으로 처리하는 등)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이번 주차에는 static은 언제 사용해야 하고 언제 지양해야 하는지에 대해 가볍게 조사한 뒤 나만의 기준을 세우고 코드를 작성하기로 했다.&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;상수는 예외 없이 static final로 관리&lt;/li&gt;
&lt;li&gt;도메인 규칙이 늘어날 경우가 있다면 지양 -&amp;gt; 1주 차 Validator로 보면, 유틸성 검증이어도 도메인 규칙이 추가되면 순수 유틸이 아니게 된다.&lt;/li&gt;
&lt;li&gt;입출력 혹은 랜덤처럼 환경에 종속적이라면 지양&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 기준을 토대로 과제에서 사용한 static을 살펴보면, Lotto 도메인의 검증 클래스와 메서드가 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;717&quot; data-origin-height=&quot;259&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZawbj/dJMcacIbNfF/KKnl4A6sSqKL4L8709rmfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZawbj/dJMcacIbNfF/KKnl4A6sSqKL4L8709rmfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZawbj/dJMcacIbNfF/KKnl4A6sSqKL4L8709rmfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZawbj%2FdJMcacIbNfF%2FKKnl4A6sSqKL4L8709rmfk%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;717&quot; height=&quot;259&quot; data-origin-width=&quot;717&quot; data-origin-height=&quot;259&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 검증 로직은 입력 값만을 기반으로 동작하는 순수 함수이며, 내부 상태를 가지지 않는다. 또한 외부 자원이나 환경에 의존하지 않기 때문에 별도의 인스턴스를 생성할 필요가 없다. 따라서 Validator를 인스턴스 레벨이 아닌 정적 유틸리티 형태로 두는 것이 적합하다고 판단했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 Validator는 Lotto 내부에서만 사용되는 내부 클래스(inner class)로 구성되어 있는데, 이 경우 각 Lotto 객체마다 검증기가 새로 생성될 이유가 없으므로 static 선언을 통해 불필요한 객체 생성을 방지하고 설계 의도를 명확히 드러낼 수 있었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;404&quot; data-origin-height=&quot;95&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWFxGI/dJMcac9f93W/WTpNDJHiUM1HyDNVecsjf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWFxGI/dJMcac9f93W/WTpNDJHiUM1HyDNVecsjf1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWFxGI/dJMcac9f93W/WTpNDJHiUM1HyDNVecsjf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWFxGI%2FdJMcac9f93W%2FWTpNDJHiUM1HyDNVecsjf1%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;404&quot; height=&quot;95&quot; data-origin-width=&quot;404&quot; data-origin-height=&quot;95&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 위에서 다뤘던 정적 메서드 들에도 static을 사용했다. 정적 메서드들 또한 순수하게 객체를 생성하는 책임만 수행하며, 외부 의존이나 상태가 없으니 사용해도 괜찮았다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;729&quot; data-origin-height=&quot;329&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KCxVV/dJMcaaKm8jc/GRXGsyH9bJvZksawK1zKMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KCxVV/dJMcaaKm8jc/GRXGsyH9bJvZksawK1zKMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KCxVV/dJMcaaKm8jc/GRXGsyH9bJvZksawK1zKMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKCxVV%2FdJMcaaKm8jc%2FGRXGsyH9bJvZksawK1zKMK%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;729&quot; height=&quot;329&quot; data-origin-width=&quot;729&quot; data-origin-height=&quot;329&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 상수 처리에 대한 예시이다. 오류 메시지들은 enum으로 관리했는데, enum 상수는 컴파일 시 자동으로 &lt;code&gt;public static final&lt;/code&gt; 형태로 처리되어 별도의 인스턴스 생성 없이 하나의 불변 객체를 전역에서 공유하는 static 상수의 형태로 동작한다.&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;static은 잘못 사용하면 도메인 책임이 모호해져 객체지향이 깨지거나 테스트가 어려워지는 등의 문제를 만들 수 있지만, 적절히 사용하면 객체 생성을 줄이고 의도를 명확히 드러내는 효과가 있기 때문에 잘 사용하기 위한 고민이 필요한 기능이라고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로도 &lt;b&gt;적절한 기준을 바탕으로 static을 활용하여 읽기 쉽고 안정적인 코드를 작성&lt;/b&gt;하기 위해 노력하려고 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Enum타입 활용하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 과제에서는 enum타입을 사용할 것을 우테코 측에서 권장하였다. 지금까지는 상수를 모아두고 사용하는 기능 정도로 생각하고 있었고, 나는 에러메시지조차 따로 상수화를 안 하고 있었기 때문에 접할 일이 없었는데 이번 기회에 제대로 학습하고 활용하기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하드코딩 대신 상수를 관리하라는 내용 또한 요구사항이었기 때문에 상수들을 enum으로 관리하는 것은 당연한 일이었고, 이번 로또 도메인 규칙에서는 아래와 같은 데이터를 저장하고 출력하는 부분이 있었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;384&quot; data-origin-height=&quot;180&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c98MvX/dJMcaiVWqHu/EqJf486BdJd4OrkDvcu1qK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c98MvX/dJMcaiVWqHu/EqJf486BdJd4OrkDvcu1qK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c98MvX/dJMcaiVWqHu/EqJf486BdJd4OrkDvcu1qK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc98MvX%2FdJMcaiVWqHu%2FEqJf486BdJd4OrkDvcu1qK%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;384&quot; height=&quot;180&quot; data-origin-width=&quot;384&quot; data-origin-height=&quot;180&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 보고 Enum 클래스로 당첨 등수를 관리해야겠다 까지는 생각하긴 했었는데 정확한 enum의 원리와 문법적 기능을 몰라 어떻게 구현해야 할지 생각이 잘 안 났다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당첨 등수를 상수로 설정한다면 각 등수가 당첨된 개수는 어디서 다룰까? 로또가 해당 등수가 되기 위한 조건들도 함께 다루는 것이 좋을 텐데 어떻게 다룰 수 있지? 함께 다룬다고 하면 특정 로또가 해당 조건을 갖추고 있는지 찾아내고 Enum 타입의 등수 값을 반환하는 게 가능한가? enum의 인스턴스 변수는 어떻게 설계해야 할까 등 여러 가지 의문이 들었다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;EnumMap&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 첫 번째로 당첨 등수와 대응되는 로또 개수를 처리할 방법으로 Map 자료구조를 떠올렸다. 처음에는 Enum 상수의 이름값을 Key로 가지도록 Map&amp;lt;String, Integer&amp;gt; 로 해야겠다 생각했는데 Enum에 대해 찾다 보니 EnumMap이라는 개념을 알게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://yeonyeon.tistory.com/195&quot;&gt;EnumMap이란?&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EnumMap은 Enum을 키로 사용하기에 최적화된 자료구조로 HashMap보다 빠르고 효율적이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;621&quot; data-origin-height=&quot;283&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/V9zrj/dJMcaelHEb9/XBxryypDqLDEAZ6SNruDq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/V9zrj/dJMcaelHEb9/XBxryypDqLDEAZ6SNruDq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/V9zrj/dJMcaelHEb9/XBxryypDqLDEAZ6SNruDq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FV9zrj%2FdJMcaelHEb9%2FXBxryypDqLDEAZ6SNruDq1%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;621&quot; height=&quot;283&quot; data-origin-width=&quot;621&quot; data-origin-height=&quot;283&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 EnumMap을 사용하여 당첨 등수와, 해당 등수가 당첨된 개수를 함께 저장하도록 하였다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;EnumType의 인스턴스 변수&lt;/h4&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;5개 이상 일치하는 경우, 보너스 번호에 대한 일치 여부&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;여기서 마지막 요소는 EnumMap의 Value로 관리하기로 하였으니, 나머지 3개 변수를 Enum에 인스턴스 변수로 함께 저장하기로 했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;368&quot; data-origin-height=&quot;70&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qkxMB/dJMcaaQ8MGI/ShEsKavSmTx4n7MLpU8tHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qkxMB/dJMcaaQ8MGI/ShEsKavSmTx4n7MLpU8tHk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qkxMB/dJMcaaQ8MGI/ShEsKavSmTx4n7MLpU8tHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqkxMB%2FdJMcaaQ8MGI%2FShEsKavSmTx4n7MLpU8tHk%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;368&quot; height=&quot;70&quot; data-origin-width=&quot;368&quot; data-origin-height=&quot;70&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 남은 건 일치하는 번호 개수와 보너스 번호 여부를 기준으로 매칭되는 등수를 찾을 수 있도록 해야 했는데 그 답은 이동욱 개발자님이 작성하신 Enum 활용기 포스팅에서 찾을 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://techblog.woowahan.com/2527/&quot;&gt;https://techblog.woowahan.com/2527/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1762522375051&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Java Enum 활용기 | 우아한형제들 기술블로그&quot; data-og-description=&quot;안녕하세요? 우아한 형제들에서 결제/정산 시스템을 개발하고 있는 이동욱입니다. 이번 사내 블로그 포스팅 주제로 저는 Java Enum 활용 경험을 선택하였습니다. 이전에 개인 블로그에 Enum에 관해 &quot; data-og-host=&quot;techblog.woowahan.com&quot; data-og-source-url=&quot;https://techblog.woowahan.com/2527/&quot; data-og-url=&quot;https://techblog.woowahan.com/2527/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/RCQrd/hyZNuscEuX/pokW2IGKtF0E5reQL1Vss0/img.jpg?width=1640&amp;amp;height=856&amp;amp;face=0_0_1640_856&quot;&gt;&lt;a href=&quot;https://techblog.woowahan.com/2527/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://techblog.woowahan.com/2527/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/RCQrd/hyZNuscEuX/pokW2IGKtF0E5reQL1Vss0/img.jpg?width=1640&amp;amp;height=856&amp;amp;face=0_0_1640_856');&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;Java Enum 활용기 | 우아한형제들 기술블로그&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;안녕하세요? 우아한 형제들에서 결제/정산 시스템을 개발하고 있는 이동욱입니다. 이번 사내 블로그 포스팅 주제로 저는 Java Enum 활용 경험을 선택하였습니다. 이전에 개인 블로그에 Enum에 관해&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;techblog.woowahan.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://jojoldu.tistory.com/137&quot;&gt;https://jojoldu.tistory.com/137&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1762522395699&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Enum 활용사례 3가지&quot; data-og-description=&quot;안녕하세요? 이번 시간엔 enum 활용사례를 3가지정도 소개하려고 합니다. 모든 코드는 Github에 있기 때문에 함께 보시면 더 이해하기 쉬우실 것 같습니다. (공부한 내용을 정리하는 Github와 세미나+&quot; data-og-host=&quot;jojoldu.tistory.com&quot; data-og-source-url=&quot;https://jojoldu.tistory.com/137&quot; data-og-url=&quot;https://jojoldu.tistory.com/137&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/rkpvY/hyZMutyitr/1hXK1PIUkGZgA96U4xEzPK/img.png?width=479&amp;amp;height=194&amp;amp;face=0_0_479_194,https://scrap.kakaocdn.net/dn/bNjXXk/hyZM6sSeVv/REL8iap9JYnKs9EIe57Yv1/img.png?width=479&amp;amp;height=194&amp;amp;face=0_0_479_194,https://scrap.kakaocdn.net/dn/TiQol/hyZMsWNuzB/uEZ8N7yxKnakVhvU8oW9t0/img.png?width=598&amp;amp;height=656&amp;amp;face=0_0_598_656&quot;&gt;&lt;a href=&quot;https://jojoldu.tistory.com/137&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://jojoldu.tistory.com/137&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/rkpvY/hyZMutyitr/1hXK1PIUkGZgA96U4xEzPK/img.png?width=479&amp;amp;height=194&amp;amp;face=0_0_479_194,https://scrap.kakaocdn.net/dn/bNjXXk/hyZM6sSeVv/REL8iap9JYnKs9EIe57Yv1/img.png?width=479&amp;amp;height=194&amp;amp;face=0_0_479_194,https://scrap.kakaocdn.net/dn/TiQol/hyZMsWNuzB/uEZ8N7yxKnakVhvU8oW9t0/img.png?width=598&amp;amp;height=656&amp;amp;face=0_0_598_656');&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;Enum 활용사례 3가지&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;안녕하세요? 이번 시간엔 enum 활용사례를 3가지정도 소개하려고 합니다. 모든 코드는 Github에 있기 때문에 함께 보시면 더 이해하기 쉬우실 것 같습니다. (공부한 내용을 정리하는 Github와 세미나+&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;jojoldu.tistory.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;p data-ke-size=&quot;size16&quot;&gt;첫 번째 포스팅의 2번째 그리고 두 번째 포스팅의 사례 2를 보면, 타입(상태)의 연산식(행위)에 대한 책임은 타입이 가지고 한 곳에서 관리해야 한다고 주장하는 것을 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;688&quot; data-origin-height=&quot;132&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVpQi0/dJMcadNRGbg/T5ADtCBnFeb7BmmbcjDuJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVpQi0/dJMcadNRGbg/T5ADtCBnFeb7BmmbcjDuJK/img.png&quot; data-alt=&quot;포스팅의 일부&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVpQi0/dJMcadNRGbg/T5ADtCBnFeb7BmmbcjDuJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVpQi0%2FdJMcadNRGbg%2FT5ADtCBnFeb7BmmbcjDuJK%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;568&quot; height=&quot;109&quot; data-origin-width=&quot;688&quot; data-origin-height=&quot;132&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;포스팅의 일부&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;654&quot; data-origin-height=&quot;293&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cR7YxE/dJMcaap4l9R/R8aZ3rvgyCcnspBELbUcyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cR7YxE/dJMcaap4l9R/R8aZ3rvgyCcnspBELbUcyk/img.png&quot; data-alt=&quot;포스팅의 일부&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cR7YxE/dJMcaap4l9R/R8aZ3rvgyCcnspBELbUcyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcR7YxE%2FdJMcaap4l9R%2FR8aZ3rvgyCcnspBELbUcyk%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;504&quot; height=&quot;226&quot; data-origin-width=&quot;654&quot; data-origin-height=&quot;293&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;포스팅의 일부&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예시를 보고 Enum Type에 메서드를 작성해서 특정 상수에 대한 value를 동적으로 설정할 수 있다는 것을 알 수 있었다. 이 기능을 함수형 인터페이스라고 하며, 이걸 내 코드에서 등수를 다루는 Enum에 적용하면, 당첨 번호 개수와 보너스 번호 여부를 체크해서 1~5등을 모두 체크할 수 있을 것 같았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 외부에서 Enum 값을 조회하는 코드를 따로 작성하게 되면 이는 상태와 행위가 분리되는 것으로 간주할 수 있고 확장성을 고려할 때 유지보수에 대한 문제가 생길 가능성이 있는 방식이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 내가 의도에 맞도록 값을 검사하고 갯수와 번호 포함 여부에 따라 boolean 값을 반환할 수 있는 인터페이스만 정하면 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://yeonyeon.tistory.com/200&quot;&gt;Predicate란?&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 포스팅을 보고 BiPredicate라는 인터페이스를 알게 되었는데, 이 방법이 미션에서 제시된 당첨 기준 탐색에 적합하다고 판단하여 채택하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;757&quot; data-origin-height=&quot;121&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/G2ayq/dJMcagRl8SC/eMdIqlNLdCXcJ2zB3ct1T1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/G2ayq/dJMcagRl8SC/eMdIqlNLdCXcJ2zB3ct1T1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/G2ayq/dJMcagRl8SC/eMdIqlNLdCXcJ2zB3ct1T1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FG2ayq%2FdJMcagRl8SC%2FeMdIqlNLdCXcJ2zB3ct1T1%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;757&quot; height=&quot;121&quot; data-origin-width=&quot;757&quot; data-origin-height=&quot;121&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 등수 조건을 조회하는 메서드를 위와 같이 작성하고, 이를 호출해서 당첨 번호와 로또의 번호 비교 및 보너스 여부를 체크해서 등수를 반환하고, 해당 등수의 카운팅을 하도록 하는 기능을 완성하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;646&quot; data-origin-height=&quot;257&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3GLUm/dJMcajtMq5D/acorGnLCYbZwpiIAVFkYa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3GLUm/dJMcajtMq5D/acorGnLCYbZwpiIAVFkYa1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3GLUm/dJMcajtMq5D/acorGnLCYbZwpiIAVFkYa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3GLUm%2FdJMcajtMq5D%2FacorGnLCYbZwpiIAVFkYa1%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;646&quot; height=&quot;257&quot; data-origin-width=&quot;646&quot; data-origin-height=&quot;257&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 구현하고 나니 도메인 개념이 스스로 판단하도록 책임을 가지게 되어 자연스럽게 느껴졌다. 내가 사용한 BiPredicate 외에도 다양한 방법으로 구현할 수 있겠지만 핵심은 데이터와 행위를 함께 가지는 구조를 의식적으로 적용하는 것이 아닐까 싶다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;도메인 값을 포맷팅 하여 출력하는 방법&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;toString? Formatter 분리?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 다뤘듯이 이번 과제는 출력에 대한 요구사항이 조금 복잡한 편이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이 출력을 위한 포맷 결정을 어디서 하는 게 옳은지에 대한 고민이 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 객체가 자기 자신을 어떻게 표현할지 결정할지, 어떻게 출력될지에 대해서는 toString을 오버라이딩해서 구현하는 것으로 알고 있었다. 다만 포맷팅이 단순 toString에서 형식을 정의하기엔 꽤나 복잡한 구조인 것들도 있었고 View레이어에서 져야 할 책임을 도메인에서 맡는 건 아닌가 하는 생각도 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이번에는 View 쪽에 Formatter 클래스를 만들고 도메인으로부터 값만 받아와서 출력 형식을 결정짓고 포맷터에서 받아온 문자열을 출력 View가 출력만 할 수 있도록 구현하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 과제에서 형식 포맷팅이 필요한 출력 사항은 총 3가지였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 랜덤으로 생성된 로또들의 6자리 번호 출력&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 각 등수별 조건에 대한 안내사항 출력&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 로또와 당첨번호, 보너스번호의 비교 후 최종 금액&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(그 외에 몇 개를 구매했는지, 수익률이 몇인지 등에 대한 출력은 그냥 문자열 + 숫자 연산 한 번이면 완성이라 상수로 분리해서 처리하면 충분했다.)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;456&quot; data-origin-height=&quot;242&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lSMTA/dJMcahbEY4o/ksIlIdJvfVOuDtb5bQggVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lSMTA/dJMcahbEY4o/ksIlIdJvfVOuDtb5bQggVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lSMTA/dJMcahbEY4o/ksIlIdJvfVOuDtb5bQggVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlSMTA%2FdJMcahbEY4o%2FksIlIdJvfVOuDtb5bQggVK%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;456&quot; height=&quot;242&quot; data-origin-width=&quot;456&quot; data-origin-height=&quot;242&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 로또에 대한 출력을 위해 출력할 숫자 리스트 복사본을 받아와서 [숫자, 숫자...] 형태로 출력하게끔 포맷팅 해주었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;756&quot; data-origin-height=&quot;535&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xda6h/dJMb995LDG1/4RsxeX3n4fQYgaBm1RPBS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xda6h/dJMb995LDG1/4RsxeX3n4fQYgaBm1RPBS1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xda6h/dJMb995LDG1/4RsxeX3n4fQYgaBm1RPBS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fxda6h%2FdJMb995LDG1%2F4RsxeX3n4fQYgaBm1RPBS1%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;607&quot; height=&quot;430&quot; data-origin-width=&quot;756&quot; data-origin-height=&quot;535&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로 각 등수별 조건을 나타내기 위해 몇 개의 숫자가 일치해야 하는지, 보너스 볼 여부에 대한 체크, 등수별 상금은 몇인지에 대해 알려주는 안내문을 처리했다. 이 경우 Ranking Enum클래스를 받아와서 각 상수명에 따라 포맷팅을 다르게 적용하도록 구현하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;650&quot; data-origin-height=&quot;208&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjNYZR/dJMcabbrwIi/ogUV25kUlyjFeLx7UZ7zJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjNYZR/dJMcabbrwIi/ogUV25kUlyjFeLx7UZ7zJk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjNYZR/dJMcabbrwIi/ogUV25kUlyjFeLx7UZ7zJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjNYZR%2FdJMcabbrwIi%2FogUV25kUlyjFeLx7UZ7zJk%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;208&quot; data-origin-width=&quot;650&quot; data-origin-height=&quot;208&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 전체 당첨 통계를 보여주기 위한 포맷터를 만들어 주었다. 파라미터로 입력받은 값에 따라 결과 정보를 재구성하여 반환하도록 만들었다. 입력받은 순서를 유지해야 했기 때문에 LinkedHashMap을 써야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 EnumMap 역시 Enum타입의 순서를 보장하기 때문에 필요 없는 포맷터일 수도 있지만, 출력에서 요구하는 등수 순서는 5등부터 1등 순으로 출력하는 것이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 Rankings enum을 정의할 때, 1등부터 5등까지로 나열하는 게 도메인 규칙에 더 자연스럽다고 생각하고 설계했기 때문에 출력할 때에는 이 열거 순서를 조정해 줄 필요가 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력하는 View 레이어에서 포맷터를 호출할 때 원하는 출력순서를 임의로 List.of로 생성하여 전달하도록 하고, 포맷터는 입력받은 순서 그대로 결과 정보를 담은 Map을 반환하도록 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 포맷터로 따로 분리하여 출력 형식을 처리하니 어떻게 표현할지를 담은 내용이 분리되어 있어 한눈에 들어오고, 도메인에서는 형식에 구애받지 않고 값 검증, 연산, 규칙만 다룰 수 있는 형태가 된 것 같아서 개인적으로 적절한 선택이었다고 본다. 만약 출력 요구사항이 변경되어도 View나 도메인 쪽은 아예 수정할 필요가 없고 포맷터만 수정하면 될 테니 적절한 책임 분리인 것 같다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; &amp;zwj;♂️ 새롭게 시도한 내용&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;구현부터 하고 테스트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 주차에는 처음으로 TDD를 적용했었다. 테스트 코드를 본격적으로 작성해 본 것도 처음이었지만, 테스트를 메인으로 구현을 맞춰서 작업하는 방식은 일반적인 구현 후 테스트와 어떤 차이가 있는지 궁금했다. 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;이번 3주 차에서는 이 부분을 분리해서 체감해보고 싶었는데 마침 2주 차의 공통 피드백 내용 중 '테스트의 장점 중 본인이 가장 공감하는 작성 이유'에 대해서 작성해 볼 것을 권장하는 내용이 있었다. 그래서 이번에는 TDD가 아닌, 일반적인 설계 -&amp;gt; 구현 -&amp;gt; 테스트 작성 순서로 접근해 봤다. 내가 느낀 장점은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 문제 해결에 집중 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트를 먼저 생각했을 때에는 입 출력을 먼저 생각하다 보니 무엇을 넣고 무엇을 받는지를 우선으로 생각했다면, 구현 과정을 먼저 작업하게 되니 &lt;b&gt;어떻게 문제를 해결할지&lt;/b&gt;에 더 집중할 수 있었다. 로직이 복잡할수록 테스트 코드부터 짜고 이에 맞추는 방식이 난이도가 더 높을 것 같았다. 이런 경우에는 기능 단위로 끊어서 구현부터 해보면서 복잡한 기능의 구현 방식을 고려하는 것도 좋은 방법이라고 느꼈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;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;&amp;nbsp;&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를 하면 피드백 루프가 짧아지니 작업 자체가 빨라질 거라고 생각했는데, 이번 주차 방식과 비교해 보면, 조금 복잡한 기능을 다룰 때나 시간이 부족할 때에는 TDD로 하는 진행 방식이 시간적 압박이 더 크게 느껴진 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 방식이 필요한 시점에 원하는 대로 테스트를 작성할 수 있고 품질에 대한 검증도 상황에 따라 그 정도를 결정할 수 있어서 부담이 훨씬 적게 느껴졌다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;결론&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 TDD도 만능은 아닌 것 같다. 분명 좋은 기술임은 느낄 수 있었지만 이를 이상적으로 적용하기 위해서는 높은 이해도와 개발 숙련도가 필요할 것 같다. 상황에 따라 좋은 방법을 선택하는 게 좋겠지만 현재 스스로를 평가할 때 구조를 잡고 로직을 짜는 능력이 부족하다고 느껴져서 TDD를 온전하게 사용하기엔 이른 것 같다. 처음 작성해 보는, 복잡한 로직을 이해하고 구현해야 하는 상황이라면 TDD보다는 구현 후 구현한 기능에 대한 단위테스트로 접근하는 게 좋겠다는 생각이 들었다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;내가 생각하는 테스트 코드의 장점&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로 느꼈던 테스트 코드의 가장 큰 장점은 &lt;b&gt;모든 기능을 완성하지 않아도 내가 구현한 기능의 검증을 빠르고 쉽게 진행할 수 있다는 점&lt;/b&gt;인 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지는 전체 기능을 먼저 다 작성해 놓고, 의존을 모두 연결한 뒤에야 처음 실행을 해보는 방식으로 작업해 왔다. 이 처음 실행에서 의도대로 되지 않으면 디버깅을 하면서 엄청 오랜 시간을 소모했고, 코드를 작성하는 도중에도 이 기능들이 정상 작동할지에 대한 확신도 없고 마지막에 다 연결하면 아주 높은 확률로 의도한 대로 작동하지 않았기 때문에 코드를 작성 중 계속 불안감을 느낄 수밖에 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 단위 테스트로 기능을 하나씩 검증하고 나서 기능들을 연결을 하니까 &lt;b&gt;각 로직이 정상적으로 동작한다는 것에 확신을 가지고 다음 기능을 있어갈 수 있었다&lt;/b&gt;. 그리고 최종적으로 기능을 연결했을 때 &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;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;인터페이스는 필요한 상황에만 추상화로 고려해 보기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 주차에는 확장 가능성을 고려하여 처음부터 인터페이스를 설계하고, 해당 인터페이스로 구현을 하며 DIP를 활용하는 방법으로 인터페이스를 활용하였는데, 이게 확장이 필요한 상황이 오면 도움이 되겠지만 그렇지 않은 상황에서는 '왜 이 부분이 인터페이스로 작성되어 있지?'라는 의문이 생길 만도 했다고 생각한다. 실제로 인터페이스 사용 이유에 대한 질문도 몇 개 받았고..&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이 내용은 사실 이번 과제에 적용한 내용보다는 다음에 적용해 볼 내용으로 보는 게 맞는 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 프리코스 과제 기간이 끝나고 나면 이번 코드도 리뷰 내용 기반으로 리팩토링을 진행하면서 추상화를 통해 구조에 대한 개선을 할 생각이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✏️ 내가 받은 피드백&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;매직넘버 상수화&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;656&quot; data-origin-height=&quot;134&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/F0Wu6/dJMcaihkSKZ/5p2GYrVkGHdO9pa3GiONKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/F0Wu6/dJMcaihkSKZ/5p2GYrVkGHdO9pa3GiONKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/F0Wu6/dJMcaihkSKZ/5p2GYrVkGHdO9pa3GiONKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FF0Wu6%2FdJMcaihkSKZ%2F5p2GYrVkGHdO9pa3GiONKk%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;134&quot; data-origin-width=&quot;656&quot; data-origin-height=&quot;134&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;불변성을 지키며 함수 설계하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로또 팩토리에서 랜덤 값을 담은 로또 리스트를 생성할 때, 매개변수로 받은 리스트를 수정하는 방식으로 구현한 코드에 대한 리뷰를 받았다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;SmartSelect_20251107_140514_Chrome.jpg&quot; data-origin-width=&quot;1526&quot; data-origin-height=&quot;1003&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfP0D0/dJMcaaXUvuR/s0F0eBWGLti9KjYTnBWShk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfP0D0/dJMcaaXUvuR/s0F0eBWGLti9KjYTnBWShk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfP0D0/dJMcaaXUvuR/s0F0eBWGLti9KjYTnBWShk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfP0D0%2FdJMcaaXUvuR%2Fs0F0eBWGLti9KjYTnBWShk%2Fimg.jpg&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;1526&quot; height=&quot;1003&quot; data-filename=&quot;SmartSelect_20251107_140514_Chrome.jpg&quot; data-origin-width=&quot;1526&quot; data-origin-height=&quot;1003&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 식으로 외부 파라미터로 리스트를 받아와서 수정하면서 사용하게 되면, 호출한 쪽의 값을 변동 시킬 수 있다. 이러면 불변성이 깨지기 때문에 지양하는 것이 좋다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;private List&amp;lt;Lotto&amp;gt; generateRandomNumberLottos(int quantity) { 
    List&amp;lt;Lotto&amp;gt; result = new ArrayList&amp;lt;&amp;gt;(); // 파라미터로 받는 리스트를 수정할게 아니라, 로컬 리스트를 생성하여 연산 후 반환 
    for (int i = 0; i &amp;lt; quantity; i++) { 
        List&amp;lt;Integer&amp;gt; numbers = Randoms.pickUniqueNumbersInRange(1, 45, 6); 
        result.add(Lotto.from(numbers)); 
    } 
    return result;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p 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-ke-size=&quot;size16&quot;&gt;이번 주차는 나름 방어적 복사와 리스트의 불변을 고려하면서 코드를 작성하는 것이 목표였는데 이 부분에서 놓친 게 굉장히 아쉽다.&lt;br /&gt;앞으론 생성을 담당하는 메서드를 만들 때 이런 접근 방식을 습관화하여 코드 안정성에 좀 더 신경을 써야겠다고 다짐했다.. (시즌 N번째 다짐..)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;객체의 생명주기와 DI 컨테이너의 역할&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1,2주 차에 이어 3주 차에서도 이어지는 컨트롤러의 책임 분산에 대한 고민이다. 이전 주차에 다른 참가자의 코드에서 객체 인스턴스를 생성하는 책임을 AppConfig로 분리하여 &lt;code&gt;Controller&lt;/code&gt;에 의존성을 주입해 주는 역할을 하는 코드롤 보게 되었는데, 인스턴스를 생성한다는 책임을 컨트롤러도 메인함수도 지지 않아도 된다는 점에서 매력적이라고 느껴서 이번 과제에도 도입을 하게 되었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;SmartSelect_20251107_141316_Chrome.jpg&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;342&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pgE0c/dJMcaiVWzXY/CgnsikQ5fkUojFKKYb9NX0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pgE0c/dJMcaiVWzXY/CgnsikQ5fkUojFKKYb9NX0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pgE0c/dJMcaiVWzXY/CgnsikQ5fkUojFKKYb9NX0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpgE0c%2FdJMcaiVWzXY%2FCgnsikQ5fkUojFKKYb9NX0%2Fimg.jpg&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;755&quot; height=&quot;342&quot; data-filename=&quot;SmartSelect_20251107_141316_Chrome.jpg&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;342&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 나는 &lt;code&gt;static&lt;/code&gt;을 사용해서 구현을 하였는데, 한번 생성되면 변경될 일도 없고 상태도 없는 유틸성 메서드이니 &lt;code&gt;static&lt;/code&gt;으로 써도 되겠다고 판단하여 위에서 다뤘던 내 기준대로 &lt;code&gt;static&lt;/code&gt;을 적용했었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;793&quot; data-origin-height=&quot;449&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4SAM5/dJMcaiPaR6H/HKNCiXapQKB7dTcMCSIFOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4SAM5/dJMcaiPaR6H/HKNCiXapQKB7dTcMCSIFOK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4SAM5/dJMcaiPaR6H/HKNCiXapQKB7dTcMCSIFOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4SAM5%2FdJMcaiPaR6H%2FHKNCiXapQKB7dTcMCSIFOK%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;793&quot; height=&quot;449&quot; data-origin-width=&quot;793&quot; data-origin-height=&quot;449&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 리뷰에서, AppConfig는 처음 메인함수에서 호출하여 컨트롤러에 주입한 이후로는 호출되지 않기 때문에 &lt;b&gt;&lt;code&gt;static&lt;/code&gt;보다는 인스턴스로 호출하는 방법은 어떤지&lt;/b&gt;에 대한 의견을 받았다. 여기서 &lt;b&gt;AppConfig는 컨트롤러에 의존성을 주입하는 DI 컨테이너&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;code&gt;static&lt;/code&gt;은 메모리 상의 고정된 함수이고, DI '컨테이너는 객체를 관리하는 설계요소'이다. static 메서드는 클래스 로더가 로딩될 때 메모리에 고정되고, 객체 상태나 생명주기와 무관하게 동작한다. 이 특성으로 인해 순수 계산 로직이나 재사용 가능한 유틸 함수에 알맞다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 AppConfig는 단순한 계산을 실행하는 클래스가 아니다. 컨트롤러 혹은 서비스에 대해, &lt;b&gt;애플리케이션 간의 의존 관계를 설정하고 객체를 관리하는 책임&lt;/b&gt;을 가진다. 이런 역할이라면, 호출 시점마다 &lt;code&gt;new&lt;/code&gt;로 만들 필요는 없어도 DI 컨테이너처럼 객체의 생명주기를 관리하는 주체로서 동작하는 것이 바람직할 것이다. 처음 만들어서 재사용은 하더라도 시스템 전반에 박제된 구조는 아니라서 static으로 쓰는 건 적합하지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;static의 판단 기준에서 상태가 없다만 볼게 아니라, 본질적으로 책임의 성격을 기준으로 하여 판단하는 게 옳은 방향인 것 같다.&lt;br /&gt;&quot;객체의 생명주기를 애플리케이션이 관리해야 하는가?&quot;를 먼저 따져보면 얼추 답이 나온다.&lt;br /&gt;DI 컨테이너와 같이 생성, 조립, 주입을 담당하면서 앱 전반적으로 의존 관계를 관리하는 경우는 인스턴스로 관리하는 싱글톤 객체로 봐야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;순수 계산, 포맷팅, 단위 변환 등 도구에 가까운 메서드 -&amp;gt; &lt;code&gt;static&lt;/code&gt; 적합&lt;/li&gt;
&lt;li&gt;객체 생명주기 관점에서(설계적으로) 책임이 있는 경우 -&amp;gt; 인스턴스로 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 리뷰를 통해 static에 대한 기준이 좀 더 바로잡힌 느낌이 든다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;정적 팩토리 메서드의 네이밍 컨벤션&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 과제에서 정적 팩토리 메서드를 다뤘는데 팩토리 메서드 네이밍에도 컨벤션이 있음은 알고 있었는데 정확한 의미는 알지 못하고 있었다. from, of 등의 키워드가 쓰이는 것은 알았는데 정확한 의미는 모르고 '~로부터 만든 클래스' 정도로 해석해서 사용했었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;577&quot; data-origin-height=&quot;289&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cWtMjW/dJMcadf1IcO/NXErY5eqvpqG8L1KNMBhG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cWtMjW/dJMcadf1IcO/NXErY5eqvpqG8L1KNMBhG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cWtMjW/dJMcadf1IcO/NXErY5eqvpqG8L1KNMBhG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcWtMjW%2FdJMcadf1IcO%2FNXErY5eqvpqG8L1KNMBhG0%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;577&quot; height=&quot;289&quot; data-origin-width=&quot;577&quot; data-origin-height=&quot;289&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 위와 같은 리뷰를 받게 돼서 찾아보니 관용적으로 사용되는 키워드들이 사용되는 상황이 정해져 있다는 걸 알게 되었다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;of()는 이미 존재하는 값을 그대로 받아 새로운 객체를 생성할 때 사용하고&lt;br /&gt;from() 은 받은 값을 다른 형태로 가공하거나 변환하여 객체를 생성할 때 사용한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를&amp;nbsp;기준으로&amp;nbsp;보면&amp;nbsp;위&amp;nbsp;코드에서는&amp;nbsp;단순하게&amp;nbsp;List &amp;lt;Integer&amp;gt;를&amp;nbsp;그대로&amp;nbsp;전달받아&amp;nbsp;Lotto&amp;nbsp;객체를&amp;nbsp;만들었기&amp;nbsp;때문에, &lt;br /&gt;실제로는 from() 보다는 of()가 더 어울리는 네이밍이었다. 앞으로&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;h2 data-ke-size=&quot;size26&quot;&gt;✨ 소감&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 고려했던 내용들이 모두 흡수되지는 못하고 있는 게 느껴져서 아쉬운 감이 있었다. 한번 사용했다고 내 것이 될 수는 없겠지만 새롭게 배우고 싶은 내용도 많은데 다른 요소들을 고려하다 보니 이전에 했던 내용도 놓치게 되었다. 그럼에도 계속 &lt;b&gt;새로운 개념을 적용하기 위해 고민하고, 그동안 받았던 피드백들을 의식하면서 스스로에게 제한을 두고 코드를 작성하면서 개발자로서 성장하고 있음을 느낄 수 있었다.&lt;/b&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프리코스 전까지만 해도 컨벤션 하나 없이 돌아가는 쓰레기만 찍어대던 내가 컨벤션을 준수하고, 객체의 역할과 책임에 대해 고민하며 더 나은 구조를 위해 신중한 설계를 하는 습관이 생겼다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 작성한 코드에 떳떳하지 못해서 누군가에게 코드를 보여주길 망설였는데 지금은 매 주마다 지난주의 나보다 더 나아지고자 적극적으로 사람들을 찾아다니며 부족한 부분에 대한 리뷰를 요청하게 되었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리뷰를 받고 피드백을 고려하며 지난 주차의 코드들을 돌아보면서 스스로의 문제점을 파악하게 되고 , 내가 잘한 부분은 무엇인지 잘못된 부분은 무엇인지를 깨닫고 다음으로 나아갈 방향을 정할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 과제에 대해 나와 비슷한 접근 방식을 시도한 리뷰어, 나와 전혀 다른 방식으로 시도했으나 배울점이 많았던 리뷰어들과 나누는 토의도 너무 재밌었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 우테코에 지원할 때에는 회사 인턴을 병행하면서 과제까지 감당할 수 있을지 걱정이 앞서기도 했고 첫 주차 잠도 제대로 못자고 미션을 할 때에는 사실 후회도 했었고 포기할까 고민도 했었지만 지금은 프리코스에 투자하는 시간이 전혀 아깝지가 않게 느껴진다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 회고를 작성하고 있는 지금 이미 4~5주차의 오픈미션이 공개된 상태인데 솔직히 예상했던 지금까지의 미션이랑 너무 달라서 당황스럽기도 하고 걱정도 많이 되는 것도 사실이다. 그럼에도 지금까지 해온 것 처럼,&amp;nbsp; 성장을 위해 몰입하고 부족한 점을 채울 수 있는 방향으로 꾸준히 나아가면 다음 회고를 작성할 때 더 성장해 있을 것이라고 믿고 정진해야겠다.  &lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;[제출 PR 링크]&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/woowacourse-precourse/java-lotto-8/pull/221&quot;&gt;https://github.com/woowacourse-precourse/java-lotto-8/pull/221&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1762522414740&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;[로또] 김선우 미션 제출합니다. by meoraeng &amp;middot; Pull Request #221 &amp;middot; woowacourse-precourse/java-lotto-8&quot; data-og-description=&quot;✨ 미션 구현 사항 정리 이번 미션에서는 사용자 입력을 기반으로 로또 구매, 당첨 번호 비교, 수익률 계산 및 출력까지의 흐름을 구현했습니다. 학습 목표 및 공통 목표 과제에서 제시된 학습 &quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/woowacourse-precourse/java-lotto-8/pull/221&quot; data-og-url=&quot;https://github.com/woowacourse-precourse/java-lotto-8/pull/221&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/BZOVk/hyZMZ1Hki8/o1hPYmiqmuITTcHM65Aj31/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bgQImc/hyZMHl5InS/KSXKgGBMH5Jw9K3LIiPVRk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/woowacourse-precourse/java-lotto-8/pull/221&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/woowacourse-precourse/java-lotto-8/pull/221&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/BZOVk/hyZMZ1Hki8/o1hPYmiqmuITTcHM65Aj31/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bgQImc/hyZMHl5InS/KSXKgGBMH5Jw9K3LIiPVRk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;[로또] 김선우 미션 제출합니다. by meoraeng &amp;middot; Pull Request #221 &amp;middot; woowacourse-precourse/java-lotto-8&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;✨ 미션 구현 사항 정리 이번 미션에서는 사용자 입력을 기반으로 로또 구매, 당첨 번호 비교, 수익률 계산 및 출력까지의 흐름을 구현했습니다. 학습 목표 및 공통 목표 과제에서 제시된 학습&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;</description>
      <category>My Story/우아한테크코스</category>
      <category>우아한테크코스8기</category>
      <category>우테코</category>
      <category>프리코스</category>
      <category>회고록</category>
      <author>_루디_</author>
      <guid isPermaLink="true">https://kimjoraeng.tistory.com/86</guid>
      <comments>https://kimjoraeng.tistory.com/86#entry86comment</comments>
      <pubDate>Wed, 5 Nov 2025 23:25:39 +0900</pubDate>
    </item>
    <item>
      <title>[회고] 우아한 테크코스 백엔드 8기 프리코스 2주차 - 자동차 경주</title>
      <link>https://kimjoraeng.tistory.com/85</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;473&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/muwWO/dJMcaaDzd3U/rKuVu8okTNKmKmsB1rRCn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/muwWO/dJMcaaDzd3U/rKuVu8okTNKmKmsB1rRCn1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/muwWO/dJMcaaDzd3U/rKuVu8okTNKmKmsB1rRCn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmuwWO%2FdJMcaaDzd3U%2FrKuVu8okTNKmKmsB1rRCn1%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;1280&quot; height=&quot;473&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;473&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;이번 과제에서는 1주차 과제에서 받았던 피드백을 최대한 반영하고자 하였다. 디스코드에 올라왔던 공통 피드백, 내 PR에 달렸던 리뷰를 통한 피드백, 다른 참가자분들 코드를 리뷰하기 위해 방문했다가 알게된 정보들 등등 새로 알게된 내용들이 너무 많았기 때문에 2주차 과제가 공개되기 전부터 빨리 이 내용을 적용해보고 싶다는 생각을 하며 기다리고 있었다.&lt;br /&gt;물론 가능하다면 모두 적용해보고 싶었지만 제대로 이해하지 못한채 무작정 남용하게 되면 오히려 독이될 수도 있다는 생각에 이번에도 우선순위를 정하고 천천히 하나씩 적용해나갔다. 새로 알게된 내용을 써보고 싶다는 생각도 있었는데 개인적으로 &lt;b&gt;지난 주차에 의식하고 적용하려고 노력했음에도 제대로 수행해내지 못했던 개념들&lt;/b&gt;에 대해 좀 더 확실히 이해하고 제대로 적용하고 싶다는 생각도 있었다.&lt;br /&gt;2주차 프리코스 저장소는 아래 링크에 있다.&lt;br /&gt;&lt;a href=&quot;https://github.com/woowacourse-precourse/java-racingcar-8&quot; target=&quot;_self&quot;&gt;&lt;span&gt;https://github.com/woowacourse-precourse/java-racingcar-8&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;GitHub - woowacourse-precourse/java-racingcar-8&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;Contribute to woowacourse-precourse/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/woowacourse-precourse/java-racingcar-8&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/Oh1QT/hyZLiNYtYw/AAAAAAAAAAAAAAAAAAAAAINmByTaVJ7ptYvmttBmNuqEamxMV0jcUkexTbS30Dag/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1761922799&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=lYZsw86dWLHq17rOOLkw%2BjbGMQE%3D&quot; data-og-url=&quot;https://github.com/woowacourse-precourse/java-racingcar-8&quot;&gt;&lt;a href=&quot;https://github.com/woowacourse-precourse/java-racingcar-8&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/woowacourse-precourse/java-racingcar-8&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://blog.kakaocdn.net/dna/Oh1QT/hyZLiNYtYw/AAAAAAAAAAAAAAAAAAAAAINmByTaVJ7ptYvmttBmNuqEamxMV0jcUkexTbS30Dag/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1761922799&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=lYZsw86dWLHq17rOOLkw%2BjbGMQE%3D');&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 - woowacourse-precourse/java-racingcar-8&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to woowacourse-precourse/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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f3c000;&quot;&gt;⚡구현 과정 요약&lt;/span&gt;&lt;/h2&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;/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;각 자동차에 대해 무작위 함수를 적용하고 그 값이 4이상인 경우 전진한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;게임이 끝나면 우승자를 출력한다(여럿인 경우 콤마(,)로 묶어서 출력한다)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️시스템 설계&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;748&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clS17e/dJMb99YXGC5/zwkd4QndBgXLk3CyZnxQK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clS17e/dJMb99YXGC5/zwkd4QndBgXLk3CyZnxQK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clS17e/dJMb99YXGC5/zwkd4QndBgXLk3CyZnxQK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FclS17e%2FdJMb99YXGC5%2Fzwkd4QndBgXLk3CyZnxQK0%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;1280&quot; height=&quot;748&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;748&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로 지난 주차에 코드를 작성하던 중 설계의 문제를 깨닫고 다시 돌아가서 &lt;b&gt;설계를 수정하고 코드를 다시 작성하고를 반복하는 과정에서 시간을 많이 소비했다&lt;/b&gt;는 생각이 들어서 이번에는 &lt;b&gt;설계를 좀 더 완성도 있게 해두고 코드 작업을 시작하자는 생각으로 설계에 시간을 많이 소비했다.&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️구현 기능 목록&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;자동차 이름과 전진 시도 횟수 입력기(View)&lt;/h4&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;입력받은 문자열 형태의 값을 각각 String타입 배열과 int타입 변수로 변환하여 반환한다.
&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;파싱하는 과정에서 이름의 길이가 5글자 이하인지 검증자동차 및 자동차 콜렉션 데이터 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Factory에서 자동차 및 자동차 콜렉션 데이터 생성
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;생성자에서 이름값을 받아 중복 여부 검증(Validator 호출)&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;/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;이 과정에서 Gate를 호출하여 랜덤 함수를 호출하고 4이상의 수가 나온 경우에만 전진 허락&lt;/li&gt;
&lt;li&gt;반복 횟수만큼 전진 시도가 끝나면 이동 거리가 최대인 차의 이름을 리스트로 반환라운드 진행 후 출력(Printer)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;전진이 진행되는 라운드마다 Printer를 호출
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프린터 호출은 Car 도메인에서 Printer를 호출하여 출력 요청우승자 출력&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;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f3c000;&quot;&gt; 미션 구현을 위해 고민했던 부분 &amp;amp; 지난주 피드백 적용&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 과제를 진행하면서 스스로 아쉬웠던 점은 고민했던 내용이 많은 것에 비해 설계가 많이 부실했던 점이었다. 지금까지는 돌아가면 되는 코드를 작성하다 보니 설계 단계가 조금 부실해도 누더기가 된 코드들을 어떻게든 이어붙이고 기능 완성까지는 할 수 있었다. 하지만 이번에는 우테코 프리코스 미션을 위해 기능적으로 구조적으로 잘 짜여진 코드를 작성하는 것을 목표로 하게 되었는데,자바 지식과 실력은 부족하고 부실한 설계 위에서 코드 작업을 진행하다 보니 &lt;b&gt;의도대로 구현이 불가능함을 뒤늦게 깨닫고 중간 중간 계속 설계로 돌아가서 수정하기를 반복&lt;/b&gt;하는 과정에서 시간을 많이 소모하며 스트레스를 많이 받았다.&lt;br /&gt;그래서 이번 주차에는 탄탄한 설계를 먼저 갖춰두고 다시 돌아와서 수정할 일 없이 코드를 쓰는 것을 목표로 주어진 미션 기간의 대부분을 설계에 투자했다. 첫 주차에는 빨리 코드 작성을 시작해야 완성할 수 있을 것 같다는 생각에 우선 README에 빠르게 기능을 나열하고 코드 작업을 시작했지만, 이번에는 노트를 펴고 각 기능과 요구사항을 어떤 방식으로 구현할 것인지 떠오르는 방법들과 각 방식의 장단점 등을 모두 적고 비교해보았다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;7376&quot; data-origin-height=&quot;3624&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b99E6S/dJMcacg44D6/gOKe7ofruUfybw3svy00b0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b99E6S/dJMcacg44D6/gOKe7ofruUfybw3svy00b0/img.png&quot; data-alt=&quot;노트에 휘갈긴 고민의 흔적...&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b99E6S/dJMcacg44D6/gOKe7ofruUfybw3svy00b0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb99E6S%2FdJMcacg44D6%2FgOKe7ofruUfybw3svy00b0%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;7376&quot; height=&quot;3624&quot; data-origin-width=&quot;7376&quot; data-origin-height=&quot;3624&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;노트에 휘갈긴 고민의 흔적...&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 코드 작성 시 고려할 요소들에 대한, 지난주부터 이어진 내용들을 의식하고 지키기 위해서 하게 되었다.&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;SRP를 위해 어디까지 분리해야할까? 그러면서도 응집도를 높이기 위해서는 무엇을 기준으로 삼아야 할까(리뷰 피드백 내용)&lt;/li&gt;
&lt;li&gt;getter를 쓰지 않으면서도 객체끼리 협력하기 위한 방법에는 무엇이 있을까&lt;/li&gt;
&lt;li&gt;&lt;code&gt;static&lt;/code&gt;을 남용하지 않아야 하는데, &lt;code&gt;static&lt;/code&gt;은 어떤 상황에서 써야할까(리뷰 피드백 내용)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Controller&lt;/code&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;시도 횟수 대한 자료형은 무엇으로 할지 (int, Integer, BigInteger)&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;일급 콜렉션으로 HashMap을 필드로 갖는 Cars만 사용하여 &amp;lt;이름, 거리&amp;gt; 형태로 저장하고 stream과 API를 통해 연산 (공통 피드백 내용)&lt;/li&gt;
&lt;li&gt;Car클래스 타입의 콜렉션을 필드로 갖는 Cars로 각 도메인에 필요한 연산에 대한 메소드를 제공(비교, 출력 등)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;콜렉션을 쓴다면 어떤 콜렉션을 쓸 것인지(&lt;code&gt;LinkedHashMap&lt;/code&gt;, &lt;code&gt;List&lt;/code&gt;)..&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;랜덤 값 체크를 도메인 내에서 validation 메서드로 처리해야할지, 따로 클래스를 분리해야할지&lt;/li&gt;
&lt;li&gt;이름 중복에 대한 체크를 순회로 처리할지, Set을 통해 자료형으로 처리할지&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;Printer를 분리하여 책임을 분산하여 SRP 고려
&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;Printer 객체의 메서드를 호출하여 자신의 정보를 매개변수로 넘기기(요청)&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;지난 주차에서 했던 대로 구분자 지정하여 순회하는 방식을 채택할지&lt;/li&gt;
&lt;li&gt;다른 리뷰어들이 활용했던 split메서드와 정규표현식을 활용해볼지&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;&lt;span style=&quot;background-color: #f89009;&quot;&gt;1. 설계는 어디까지 해야하는걸까&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완성도 높은 설계를 위해 설계방식에 대한 고민과, 이번 주차 과제에서 적용하고 싶은 새로운 개념에 대한 공부를 병행하면서 &lt;b&gt;만족스러운 설계를 간신히 완성하고 시간을 확인했을 때, 코드 한줄도 못쓴 상태로 금요일이 되어있었다&lt;/b&gt;.&lt;br /&gt;평일 출퇴근 이후 과제에 할애할 수 있는 시간이 많지 않았기 때문에, 내 나름대로 퇴근 이후 모든 개인 시간을 갈아넣었지만 시간은 항상 부족하게 느껴졌다. 그리고 그제서야 부족한 시간 속에 너무 설계만 붙잡고 있었던게 아닌가 하는 후회가 몰려왔다. 주말에도 일정이 있었기 때문에 시간안에 미션을 완성할 수 있을지 알 수 없는 상태로 시간에 쫓기며 급하게 코드 작성을 했다. 과제 내용 자체는 크게 복잡하지 않았지만 내가 공부한 내용들을 적용하고 시행착오를 겪는 시간이 얼마나 걸릴지 가늠할 수 없어서 시간내로 완성할 수 있을지 확신할 수 없었다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;결과적으로 일요일 새벽 기능을 완성하고 월요일 밤에 최종 리팩토링과 검수를 마친 후 아슬아슬하게 제출할 수 있었지만 &lt;b&gt;완벽한 설계를 추구하며 설계만 붙잡고 있는 것 또한 옳은 방법은 아닌 것 같다&lt;/b&gt;는 생각을 가지게 되었다.&lt;b&gt;나름 자신있게 완벽하다고 생각한 설계를 가지고 작업을 시작했으나 한번의 수정 없이 작업을 할 수 있던 것은 아니었다.&lt;/b&gt; 놓쳤던 메소드가 추가로 필요하기도 했고, 고려하지 못했던 접근 권한에 대한 문제나 자잘한 네이밍 등 수정할 요소들은 계속 나타났다.&lt;br /&gt;물론 전체적인 틀이 이미 짜여진 상태에서 시작한 덕분에 작업 시간 자체는 지난 주차보다 절반은 줄어들었던 것 같다. 하지만 항상 이번처럼 운좋게 시간안에 완성할 수 있을 거라는 보장은 없기 때문에 &lt;b&gt;설계 시간과 작업시간의 분배를 어떻게 하는게 좋을지&lt;/b&gt;에 대한 고민을 하게 되었다. 이 고민은 시행착오를 여러번 겪어야 결론을 낼 수 있을 것 같다.   &lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #f89009;&quot;&gt;2. 테스트코드와 TDD(feat. &lt;code&gt;JUnit&lt;/code&gt;, &lt;code&gt;AssertJ&lt;/code&gt;)&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 테스트 이름에 대한 고민 정도만 PR리뷰로 받게 되어서 네이밍을 좀 더 신경쓰자 정도로 생각했는데 &lt;b&gt;과제에 제시된 학습목표가 '테스크 도구 사용법에 대한 숙지 및 프로그램 정상 작동 테스트'로 결정&lt;/b&gt;되면서 테스트 코드 자체에 대한 학습이 필요하다고 생각했다.&lt;br /&gt;하지만 과제와 함께 첨부된 테스트 도구 가이드를 전부 읽기에는 시간이 부족하다고 판단했다. 그래서 우선 출퇴근 시간 동안 우아한테크 전 기수 참가자들의 테코톡 발표에서 다뤄진 테스트 코드 및 TDD에 대한 영상들을 시청하고 집에와서 내용을 정리하였다.&lt;br /&gt;(시청한 영상은 피카의 TTD와 단위 테스트, 제이의 단위 테스트, 비버의 JUnit 등이었다)&lt;br /&gt;간단히 요약해보자면&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;✏️ TDD란 무엇인가?&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;테스트 코드를 먼저 만들고 프로덕션 코드를 나중에 만드는 개발 방법을 말한다&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;기존 프로세스 : 설계 &amp;rarr; 개발 &amp;rarr; 테스트 코드&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;TDD 프로세스 : 설계 &amp;rarr; 테스트코드 &amp;rarr; 개발&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;✏️ TDD를 사용하는 이유&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;테스트 코드의 장점 : 변화에 대한 두려움을 줄여준다, 디버깅 시간을 줄여준다, 동작하는 문서 역할을 한다&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;TDD의 장점 : 자연스레 테스트 커버리지가 높아진다 &amp;rarr; 반드시 장점인 것은 아님,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;오버엔지니어링 방지, 설계에 대한 피드백이 빠르다&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;✏️ TDD 주의할점&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;TDD는 설계방법론이 아니며, 높은 응집도를 유도하지는 않는다. 단일 책임 원칙과 인터페이스 분리 원칙 위배에 대해서도 어떤 신호도 주지 않으며 인터페이스 일관성을 도출하지 않는다. 즉, TDD에만 의존하면 테스트 하기만 좋은, 안좋은 설계가 될 수 있다. (설계는 별개!)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;✏️ 단위 테스트란?&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;가장 작은 단위의 테스트로 일반적으로 메서드 레벨에 대한 테스트를 가리킨다. 검증이 필요한 코드에 대해 테스트 케이스를 작성하는 절차 또는 프로세스로 Unit Testing은 테스트 코드가 목적 코드의 완전성을 입증 해주기 때문에, 테스트 코드 그 자체만으로 주요한 가치가 있다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;단위 테스트의 목적은 문제점 발견, 쉬운 변경, 품질 향상, 코드 문서화, 최신상태 유지 등이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;✏️ given-when-then패턴&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;테스트 코드 작성의 표현 방식으로 이 패턴을 통해 더 쉽게 읽을 수 있고 유지보수 하기 좋은 테스트 코드 작성이 가능하다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;- given : 테스트를 위해 준비하는 과정, 테스트에 사용하는 값을 정의&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;- when : 테스트하고자 하는 기능을 실행&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;- then: 기능을 실행한 후 결과를 검증&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 주차에 디버깅에서 시간을 많이 잡아먹기도 했고, 코드가 예상대로 작성되지 않아 설계를 수정해야 하는 과정에서 두려움을 느끼고 있었기 때문에 테스트 코드의 장점이 크게 와닿았다. 그리고 설계에 대한 피드백 과정에서 시간을 많이 할애하며 스트레스를 받았던 부분을 TDD를 통해 해결할 수 있지 않을까 하는 생각에 단순 테스트 코드만 적용하지 않고 TDD에도 도전해보기로 했다.&lt;br /&gt;물론 처음 적용하는 과정이다 보니 엉성하기도 하고 시행착오도 많이 겪었다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;테스트 코드의 작성 순서는?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 내용부터 고민이 되었다. 단위 테스트로 작성하니까 다른 기능의 완성과는 별개로 테스트를 작성할 수는 있겠으나, 아무거나 먼저 작성할 수 있게 되니가 무엇부터 작성해야 하는지가 고민이었다. 그래서 테스트 작성의 우선 순위에 대해 찾아보고 내 설계에서는 무엇부터 적용해야 할지를 생각해보았다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2299&quot; data-origin-height=&quot;3353&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FzWaH/dJMcagcIj0n/85JhkmgulKbhZZeaoOjclk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FzWaH/dJMcagcIj0n/85JhkmgulKbhZZeaoOjclk/img.png&quot; data-alt=&quot;역시 고민 정리는 노트에.. 아날로그 최고&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FzWaH/dJMcagcIj0n/85JhkmgulKbhZZeaoOjclk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFzWaH%2FdJMcagcIj0n%2F85JhkmgulKbhZZeaoOjclk%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;326&quot; height=&quot;475&quot; data-origin-width=&quot;2299&quot; data-origin-height=&quot;3353&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;역시 고민 정리는 노트에.. 아날로그 최고&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 내린 결론은 MVC에서도 Model을 중점으로, 그중에서도 공개 API 위주로 작성하며 불안정한 외부요인(이번 과제에서는 랜덤 함수 API)에 먼저 테스트를 작성하기로 했다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Mock? Stub?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 클래스 안에서 Given-When-Then만 잘 설정해주면 되는 요소들은 비교적 테스트 코드를 어떻게 짤지 구상하는 것이 어렵지 않았다. 하지만 출력을 직접 확인해야 하는 경우에는 그냥 System.out.println()을 쓰면 결과를 assert할 수 없었고 어떻게 해야할지 고민에 빠졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색을 해보니 보통 이런 상황에서는 Mockito라이브러리를 통해 &lt;code&gt;@Spy&lt;/code&gt;, &lt;code&gt;@Mock&lt;/code&gt;, &lt;code&gt;verify()&lt;/code&gt;어노테이션 등을 쓰면서 Mocking이나 Stubbing을 구현하는 것 같았다. 하지만 우테코 미션에서는 기본 제공된 &lt;code&gt;AssertJ&lt;/code&gt;, &lt;code&gt;JUnit&lt;/code&gt;을 제외한 도구의 사용이 제한되어 있어서 내가 사용할 수 있는 방법은 아니었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고민 끝에 그냥 &lt;b&gt;직접 Spy 클래스를 만들면 되지 않을까? &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;708&quot; data-origin-height=&quot;224&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUsE9t/dJMcai2FBlE/pSHoB7NSJ3Jp1FYBJfTzkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUsE9t/dJMcai2FBlE/pSHoB7NSJ3Jp1FYBJfTzkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUsE9t/dJMcai2FBlE/pSHoB7NSJ3Jp1FYBJfTzkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUsE9t%2FdJMcai2FBlE%2FpSHoB7NSJ3Jp1FYBJfTzkK%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;616&quot; height=&quot;195&quot; data-origin-width=&quot;708&quot; data-origin-height=&quot;224&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 출력 대신 기록만 하고 테스트에서는 그 기록값을 assert한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;671&quot; data-origin-height=&quot;187&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/W4YuJ/dJMcajmYnvH/vtClMCiDSmvK6T7pXJmPe0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/W4YuJ/dJMcajmYnvH/vtClMCiDSmvK6T7pXJmPe0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/W4YuJ/dJMcajmYnvH/vtClMCiDSmvK6T7pXJmPe0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FW4YuJ%2FdJMcajmYnvH%2FvtClMCiDSmvK6T7pXJmPe0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;671&quot; height=&quot;187&quot; data-origin-width=&quot;671&quot; data-origin-height=&quot;187&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;winner 출력 또한 같은 방식으로 구현하여 assert가 가능한 테스트를 작성하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;681&quot; data-origin-height=&quot;301&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3pQ6q/dJMcajUOkVp/zrzsPQ8CAAKZdNRKvwcLS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3pQ6q/dJMcajUOkVp/zrzsPQ8CAAKZdNRKvwcLS1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3pQ6q/dJMcajUOkVp/zrzsPQ8CAAKZdNRKvwcLS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3pQ6q%2FdJMcajUOkVp%2FzrzsPQ8CAAKZdNRKvwcLS1%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;629&quot; height=&quot;278&quot; data-origin-width=&quot;681&quot; data-origin-height=&quot;301&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 랜덤 함수의 결과에 따라 전진 여부를 결정 짓는 Gate 여부에 따라 다른 동작을이 상호작용하는 테스트 또한 필요했기 때문에&amp;nbsp; 위와 같이 가짜 Gate를 구현해두었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;779&quot; data-origin-height=&quot;394&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k4Axm/dJMcaj8lxK6/d0f3nU0dTTJRUJEMcU4byK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k4Axm/dJMcaj8lxK6/d0f3nU0dTTJRUJEMcU4byK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k4Axm/dJMcaj8lxK6/d0f3nU0dTTJRUJEMcU4byK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk4Axm%2FdJMcaj8lxK6%2Fd0f3nU0dTTJRUJEMcU4byK%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;643&quot; height=&quot;325&quot; data-origin-width=&quot;779&quot; data-origin-height=&quot;394&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 식으로 구현하여 출력과 랜덤 같은 요소들에 대해 제어할 수 있게 되었다. 결국 역할을 추상화하고 테스트에서 그 역할을 다른 객체로 대체하면 테스트 코드를 작성할 수 있는 것이다.&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;도구를 사용하여 빠르게 넘겼다면 이렇게 깊은 생각을 하지 못했을 지도 모르지만 직접 구현하여 사용하니 각 테스트 과정에서 클래스의 동작 흐름을 머릿속으로 구상하고 예상되는 값이 어디서 어떻게 넘어오고 어떤 처리를 해야하는지를 생각하는 과정에서 내 설계에 대해 더 깊게 이해하게 되었다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;그리고 개인적으로 테스트 코드 없이 작성하던 때에는 일단 입출력이 있어야 코드 실행을 테스트 할 수 있으니 항상 입출력부터 처리하고 다음 코드를 작성해 나갔는데, &lt;b&gt;테스트 코드를 먼저 작성하게 되니 입출력이 없어도 기능을 테스트 할 수 있게 되어서 중요한 메소드들을 먼저 작성하면서 틀을 갖춰 나갈 수 있었던 점&lt;/b&gt;이 편리하게 느껴졌다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #f89009;&quot;&gt;3. 인터페이스 활용&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 1,2주차 미션을 진행하면서 공통적으로 했던 고민 중 하나가&lt;b&gt;&amp;nbsp;어느 정도의 확장성을 고려한 설계를 해야할까?&lt;/b&gt;&amp;nbsp;였다. 아무래도 2주차까지의 미션은 요구사항이 상세하지 않으면서도 명시되지 않은 부분에 대해서는 참가자 스스로 판단하여 작성하도록 하기 때문에 오히려 이 부분에서 어디까지 고려해서 코드를 작성해야 할지 어려움을 겪었다.&lt;br /&gt;지난 주차 리뷰들을 돌아다니면서 인터페이스를 사용하는 참가자 분들을 보고 많은 인사이트를 얻었다. 자바의 문법을 공부하면서 C++과 가장 큰 차이점이 인터페이스라고 생각했었는데, 실제로 인터페이스를 활용하여 확장성을 고려한 설계를 하는 부분이 자바스럽게 코드를 짜는 것 같다는 느낌이 들었다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;그리고 TDD에 대한 내용을 공부하면서도&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;✏️ TDD를 실패하는 이유&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;코드가 이루고자하는 가치나 기능을 테스트하기 보다 그 기능을 어떻게 구현하고 있는지를 테스트하기 때문에, 결국 테스트 케이스들이 구현체와 결합도가 높아진다. 구현체들을 리팩토링하면 결합되어있는 테스트 케이스들이 모두 깨져버린다&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;따라서 &lt;u&gt;구현체가 아닌 설계(인터페이스)를 테스트해야 구현체가 수정을 하게 되어도 테스트가 깨지지 않는다&lt;/u&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 내용을 접하고 이번에는 인터페이스를 활용해서 설계를 해보기로 결정했다.&amp;nbsp;&lt;br /&gt;그렇게 내 설계에서는 Validator, Printer, ExcutionGate 3가지 인터페이스를 사용하게 되었다. 결과적으로는 2번 항목에서 다룬 것 처럼 테스트 코드 작성에서 인터페이스를 활용해 코드를 작성하고 수월한 테스트 코드 작성을 경험할 수 있었다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;그리고 이번 2주차 리뷰에서 다음과 같은 질의응답을 하게 되었는데,&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;841&quot; data-origin-height=&quot;786&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3Kgfa/dJMcad776gY/ZnVTXA4GIxPGMUi0b1ny50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3Kgfa/dJMcad776gY/ZnVTXA4GIxPGMUi0b1ny50/img.png&quot; data-alt=&quot;소중한 리뷰 감사합니다 (__)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3Kgfa/dJMcad776gY/ZnVTXA4GIxPGMUi0b1ny50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3Kgfa%2FdJMcad776gY%2FZnVTXA4GIxPGMUi0b1ny50%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;657&quot; height=&quot;614&quot; data-origin-width=&quot;841&quot; data-origin-height=&quot;786&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;소중한 리뷰 감사합니다 (__)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터페이스의 장점들을 활용해보자! 라는 생각으로 인터페이스를 도입하긴 하였으나 정작 명확한 기준점을 가지지 못하고 인터페이스를 설계한 것은 아쉬운 점이었던 것 같다. 결국 '확장할 수 있게 만든다'는 말은 쉽지만, 어디까지 확장성을 고려하느냐가 중요한 거니까.. 물론 이번엔 인터페이스를 써보는 경험을 해보고 싶은 개인적인 욕심이 어느정도 반영된 것이 있지만 &lt;b&gt;앞으로는 역할이 분리되고 교체 가능한 상황에서 사용하는 것&lt;/b&gt;이 좋겠다는 생각이 들었다.&lt;br /&gt;또한 인터페이스를 사용할 때 계층에 대한 고민이 부족했던 것 같다. 모델측에 관련된 내용을 Validation하기 때문에 모델 계층에서 인터페이스를 정의하였으나 확장성을 고려한다면 리뷰어님 말대로 글로벌에서 해당 인터페이스를 다루는 것이 좋았을 것이다.&lt;br /&gt;다양한 상황에서 활용을 해보고 적절한 사용법의 감을 찾아갈 필요가 있을 것 같다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #f89009;&quot;&gt;4. 일급 컬렉션 그리고 팩토리 패턴&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 주차에는 일급 객체에 대한 도입을 시도했다면 이번 주차에는 여러 대의 자동차를 저장하는 일급 컬렉션에 대해 관리하기 위해 일급 컬렉션에 대해 공부하고 적용해보려는 시도를 했었다.&lt;br /&gt;&lt;a href=&quot;https://jojoldu.tistory.com/412&quot; target=&quot;_self&quot;&gt;&lt;span&gt;이 포스팅&lt;/span&gt;&lt;/a&gt;을 통해 학습하였는데 간단히 요약하면&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;✏️ 일급 컬렉션(First Class Collection)이란? &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;하나의&lt;b&gt; 컬렉션을 클래스로 감싸고, 해당 컬렉션과 관련된 검증, 연산, 규칙을 그 클래스 내부에서 책임지게 하는 설계 방식&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;단순히 자료구조를 담는 것이 아니라, &lt;b&gt;도메인에 맞는 의미 있는 컬렉션으로 만들기 위함&lt;/b&gt;이다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;- 비즈니스 규칙을 컬렉션 내부에서 보장할 수 있고 &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;- 컬렉션을 불변으로 유지하기 쉬우며&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;- 데이터(상태)와 로직(행위)을 한 곳에서 관리할 수 있고 &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;- 변수명이 아닌 클래스 이름으로 의도를 표현할 수 있다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;이를 통해 로직 중복, 검증 누락, 예상치 못한 수정 사이드 이펙트를 크게 줄일 수 있다. 결론적으로, 일급 컬렉션은 읽기 쉽고, 안전하고, 리팩토링하기 쉬운 객체지향적인 코드를 만들기 위한 핵심 도구이다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 이번 미션에서는 자동차들을 관리하는 Cars라는 클래스로 감싸며 List&amp;lt;Car&amp;gt; 타입의 cars 를 멤버로 갖는 컬렉션을 만들었다. 가장 크게 체감한 부분은 책임이 명확해졌다는 것이다. 이전에 작성한 방식에서는 컨트롤러나 서비스로 직접 도메인을 조작했지만 이번에는 모든 행위가 Cars 내부에서 처리된다. (이동, 우승자 계산, 검증)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;628&quot; data-origin-height=&quot;462&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btZLo7/dJMcaiawU29/yKUm6XWITJVq2tBk9EDPL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btZLo7/dJMcaiawU29/yKUm6XWITJVq2tBk9EDPL0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btZLo7/dJMcaiawU29/yKUm6XWITJVq2tBk9EDPL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtZLo7%2FdJMcaiawU29%2FyKUm6XWITJVq2tBk9EDPL0%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;570&quot; height=&quot;419&quot; data-origin-width=&quot;628&quot; data-origin-height=&quot;462&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 도메인에 관련된 로직이 모이면서 자동차 집합에 대한 책임을 스스로 지게 하는 자연스러운 표현이 가능했다.&amp;nbsp;&lt;br /&gt;하지만 일급 컬렉션 만으로는 해결되지 않는 부분도 있었다. 가장 큰 문제는 &lt;b&gt;Cars객체를 생성하는 과정&lt;/b&gt;이었다. 입력값으로부터 Car객체들을 만드는 과정이 Cars안에서 이루어지면 책임이 모호해지고 검증 로직과의 의존이 생겨 테스트에도 문제가 생기게 된다. 즉,&lt;b&gt; Cars는 &quot;이미 존재하는 자동차 집합&quot;을 표현해야 하는데 &quot;자동차를 어떻게 만들어낼 것인가&quot;까지 떠맡는 것은 부자연스러운 구조였다.&lt;/b&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;CarsFactory 도입&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 자동차를 생성하는 책임을 담당하는 클래스를 만들어 볼까 고민하다가 &lt;a href=&quot;https://kimjoraeng.tistory.com/50&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;이전 디자인 패턴 스터디&lt;/span&gt;&lt;/a&gt;에서 다루었던 팩토리 패턴이 떠올랐다. CarsFactory라는 클래스로 분리하면서 다음과 같은 의도를 명확하게 할 수 있었다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자동차 생성은 Factory를 통해서 이루어진다&lt;/li&gt;
&lt;li&gt;외부에서는 new Cars, new Car를 직접 호출하지 않는다&amp;nbsp;&lt;/li&gt;
&lt;li&gt;검증 &amp;rarr; 생성 &amp;rarr; 조립 과정의 흐름이 한 통로로만 진행된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 CarsFactroy를 자동차 도메인의 입구 역할로서 사용할 수 있게 되었다.&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;681&quot; data-origin-height=&quot;382&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KLUFl/dJMcacBnQmD/N0S6BsKkxhNUCYwk1IJIok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KLUFl/dJMcacBnQmD/N0S6BsKkxhNUCYwk1IJIok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KLUFl/dJMcacBnQmD/N0S6BsKkxhNUCYwk1IJIok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKLUFl%2FdJMcacBnQmD%2FN0S6BsKkxhNUCYwk1IJIok%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;681&quot; height=&quot;382&quot; data-origin-width=&quot;681&quot; data-origin-height=&quot;382&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조를 통해 Cars는 자동차를 어떻게 움직이고, 누가 우승자인지 판단하는 본연의 역할만 담당하고, 중복 이름 검증에 대한 책임도 덜 수 있게 되었다.&amp;nbsp;CarsFactory에서는 입력값을 어떻게 해석하고 Car를 어떻게 구성할지 에 대해서 담당하게 된다. 생성과 이용을 명확히 분리하면서 테스트 하기에도 수월해졌다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;이 구조는 이번 설계에서 가장 마음에 드는 부분이었는데 이 부분이 리뷰어 분들에게서도 좋은 평가를 받았다. 도메인 검증의 책임을 덜어주는 부분이 좋은 평가를 받은 것 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;848&quot; data-origin-height=&quot;521&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bK3VgD/dJMcahvVl8X/nK9uVIVwfOIZMynKN3Zky1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bK3VgD/dJMcahvVl8X/nK9uVIVwfOIZMynKN3Zky1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bK3VgD/dJMcahvVl8X/nK9uVIVwfOIZMynKN3Zky1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbK3VgD%2FdJMcahvVl8X%2FnK9uVIVwfOIZMynKN3Zky1%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;755&quot; height=&quot;464&quot; data-origin-width=&quot;848&quot; data-origin-height=&quot;521&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;h4 data-ke-size=&quot;size20&quot;&gt;팩토리 설계에서 아쉬웠던 점&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 팩토리를 단일 진입점으로서 설계를 하면서 놓쳤던 부분이 존재했는데, Car와&amp;nbsp; Cars를 public으로 작성해두어서 팩토리를 거치지 않고도 원한다면 호출하여 사용할 수 있는 상태의 코드였던 것이다.&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;801&quot; data-origin-height=&quot;377&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/db6ZhK/dJMcab3xS6R/KP7dQY9A3wpbErF7TBrdHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/db6ZhK/dJMcab3xS6R/KP7dQY9A3wpbErF7TBrdHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/db6ZhK/dJMcab3xS6R/KP7dQY9A3wpbErF7TBrdHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdb6ZhK%2FdJMcab3xS6R%2FKP7dQY9A3wpbErF7TBrdHK%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;589&quot; height=&quot;277&quot; data-origin-width=&quot;801&quot; data-origin-height=&quot;377&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 의도한 대로 단일 진입점으로서 팩토리 메서드를 강제하기 위해서는 해당 클래스들을 domain 패키지의 package-private으로 변경하는 것이 좋았을 것 같다. 다음에 팩토리를 사용할 때에는 이부분을 기억해둬야겠다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #f89009;&quot;&gt;5. 새로운 개념에 대한 도입을 결정하는 기준은?(Rule of Three)&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 주차 리뷰를 진행하면서 다양한 참가자분들의 좋은 코드를 보고 나도 적용해보고 싶다는 생각이 드는 부분이 꽤 있었다. 특히 에러메시지를 상수화하고 별도의 클래스나 enum으로 관리하는 패턴을 보고 이걸 내 코드에 적용해보면 어떨까 하는 생각이 들었다. &lt;br /&gt;처음에는 좋아 보이니까 적용을 고려 했지만, 막상 적용하려니 필요에 의해 적용하는 행동이 아니다보니 지금 써도 형태만 따라 하고 있는게 아닌가 하는 의문이 생겼다.&lt;br /&gt;&lt;br /&gt;그러던 중 과거 프로젝트를 진행하면서 리팩토링을 하는 기준에 대해 고민하던 시절 OKKY에 질문을 남겼을 때 &lt;a href=&quot;https://victoria-k.tistory.com/entry/What-is-Refactoring-When-to-Refactor&quot; target=&quot;_self&quot;&gt;&lt;span&gt;Rule Of Three&lt;/span&gt;&lt;/a&gt;에 대해 설명해주었던 내용이 기억났다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;Rule of Three&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;: 어떤 중복이나 불편이 세 번 이상 반복될 때, 그 때가 리팩토링 또는 새로운 개념 도입의 적절한 시점이다.&amp;nbsp;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 두번 나타나는 중복은 우연일 수 있고 다시 나타날지 알 수 없기 때문에 무작정 해결을 위한 코드를 도입하는 것은 오버 엔지니어링일 수도 있다. 그러다 세 번 반복되면 문제의 구조가 보이고 실질적 효과가 있는 리팩토링을 하는 시점으로 볼 수 있다는 내용이다. 이 내용을 다시 되새기며 기준으로 세우기로 했다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;에러 메시지 상수화의 경우, 현재 미션에서는 검증 경우의 수가 많지 않고 규칙 변경 가능성도 크지 않기 때문에 지금 단계에서 에러 메시지 클래스를 만드는 것은 오히려 추상화 비용이 더 큰 경우에 해당되었다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;지금 필요한가? 를 고민했을 때에 아니라는 답이 나와서 도입하지 않았다. 만약 다음 미션에서 검증 케이스가 늘고 에러 메시지가 여러 곳으로 퍼진다면, 중복되는 메시지가 많은 곳에 필요로 하고 메시지 변경 시 영향 받는 범위가 넓어진다면 그때는 도입하는게 옳다고 판단했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #f89009;&quot;&gt;6. getter없이 객체의 정보를 활용하려면...&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 주차부터 객체지향 생활체조 원칙으로 getter를 지양하라는 내용을 접하면서 getter에 대해 고민이 많아졌다. getter로 인해 private으로 설정해둔 값을 밖에서 확인 가능하면 은닉화도 깨지고, 외부에서 getter를 통해 도메인에 접근 가능하게 되면 캡슐화도 깨지고.. getter가 만악의 근원인 것 처럼 느껴졌다.&lt;br /&gt;그래서 내 코드에서는 다음과 같이 getter의 문제를 줄이기 위해 노력했다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Printer는 도메인에서 요청&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Car의 정보를 출력하는 기능을 구현할 때, 출력 부분은 View에 해당하니까 분리를 하고 싶다고 생각은 하였으나 getter를 통해 바로 프린터에게 값을 넘겨주는 것이 public상태로 노출되는 것이나 다름 없으니 피하고 싶었다. 그렇다고 도메인 내에서 처리하기엔 출력을 도메인에서 처리하는 것은 뷰의 역할이 모호해지는 것이 아닌가 하는 생각도 들었다.&lt;br /&gt;그래서 Printer를 도메인에서 호출(포트/어댑터 방식) 하게 만들었다. 위임을 하는 방식이라도 책임이 있는 것은 사실이지만, 요청에 대한 책임이 생기는 것 보다 public getter를 사용하는 것을 더 피하고 싶었다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Cars의 우승자 색출&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;371&quot; data-origin-height=&quot;86&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bL8Oac/dJMcahvVndE/fCZfTkBKGOm6UNbNnHUTUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bL8Oac/dJMcahvVndE/fCZfTkBKGOm6UNbNnHUTUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bL8Oac/dJMcahvVndE/fCZfTkBKGOm6UNbNnHUTUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbL8Oac%2FdJMcahvVndE%2FfCZfTkBKGOm6UNbNnHUTUk%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;371&quot; height=&quot;86&quot; data-origin-width=&quot;371&quot; data-origin-height=&quot;86&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 Cars에서 우승자 색출을 위해 계산을 하려다 보니, Car 정보를 외부에 안내놓고 Cars에서만 접근하 수 있도록 package-private 접근으로 적용하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;247&quot; data-origin-height=&quot;137&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKox6e/dJMcac2sdno/gttBWMUE1eER5sdaOXr4Nk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKox6e/dJMcac2sdno/gttBWMUE1eER5sdaOXr4Nk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKox6e/dJMcac2sdno/gttBWMUE1eER5sdaOXr4Nk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKox6e%2FdJMcac2sdno%2FgttBWMUE1eER5sdaOXr4Nk%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;247&quot; height=&quot;137&quot; data-origin-width=&quot;247&quot; data-origin-height=&quot;137&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;424&quot; data-origin-height=&quot;252&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWIsBp/dJMcahvVndz/NxVVgPv2KcFJv6zmh1IENK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWIsBp/dJMcahvVndz/NxVVgPv2KcFJv6zmh1IENK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWIsBp/dJMcahvVndz/NxVVgPv2KcFJv6zmh1IENK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWIsBp%2FdJMcahvVndz%2FNxVVgPv2KcFJv6zmh1IENK%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;424&quot; height=&quot;252&quot; data-origin-width=&quot;424&quot; data-origin-height=&quot;252&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;내 방식의 한계점과 개선점&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력은 본질적으로 뷰 관심사인데 도메인에서 프린터를 호출하고 있는 상태임은 변함이 없다. 이로 인해 역할 경계가 모호해진다. 그래서 내 방식이 답은 아닌 것 같은데 getter를 어떻게 해야 최대한 문제 없이 사용할 수 있을까에 대한 고민으로 이어졌다.&lt;br /&gt;그러던 중 리뷰를 통해 어느정도 답을 얻었는데,&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;799&quot; data-origin-height=&quot;328&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oClDr/dJMcaiImSeu/ymndQdBzRfqD4qiGW88hak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oClDr/dJMcaiImSeu/ymndQdBzRfqD4qiGW88hak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oClDr/dJMcaiImSeu/ymndQdBzRfqD4qiGW88hak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoClDr%2FdJMcaiImSeu%2FymndQdBzRfqD4qiGW88hak%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;641&quot; height=&quot;263&quot; data-origin-width=&quot;799&quot; data-origin-height=&quot;328&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;그러니까 결국 get이 어디에서 어떻게 사용되는지에 따라서 필요한 경우도 있다는 것이다. (우테코에서도 DTO등에는 getter 사용을 허가한다)&amp;nbsp;&lt;br /&gt;그리고 이런 내용을 고민하던 중 &lt;a href=&quot;https://velog.io/@backfox/getter-%EC%93%B0%EC%A7%80-%EB%A7%90%EB%9D%BC%EA%B3%A0%EB%A7%8C-%ED%95%98%EA%B3%A0-%EA%B0%80%EB%B2%84%EB%A6%AC%EB%A9%B4-%EC%96%B4%EB%96%A1%ED%95%B4%EC%9A%94&quot; target=&quot;_self&quot;&gt;&lt;span&gt;&amp;nbsp;다음의 포스팅을 발견&lt;/span&gt;&lt;/a&gt;했다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;내용을 아주아주 간단히 요약하면 다음과 같다.(쉽고 재밌게 유용한 내용을 잘 풀고 있는 원글을 보는 것을 추천한다)&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;getter를 쓰는 것이 문제가 되는 이유 : 도메인 객체를 사용할 때 지켜야 할 규칙들을 무시하고&amp;nbsp;&lt;b&gt;외부의 사용자가 객체 내부의 필드를 직접 가져다 쓸 수 있게 되면 그 규칙을 무시할 수 있기 때문&lt;/b&gt;에 캡슐화, 모둘화가 깨지면서 안정성이 심각하게 무너진다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;객체의 필드를 public으로 해두면 위의 문제점이 나타나며, 필드의 접근자를 private으로 하고 getXxx()형태의 getter를 사용하더라도, 필드를 public으로 공개하는 것과 다를 바가 없는 구조라면 같은 문제가 일어난다.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;결국 객체의 구성 요소를 외부로 빼내서 외부에서 조작하게 만드는 설계 구조가 문제를 일으키는 것이다.&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;핵심은 &amp;ldquo;getter 금지&amp;rdquo;가 아니라 어디서, 무엇을 위해 쓰느냐였다. 연산&amp;middot;판단은 도메인 내부에서 수행하고, 외부(출력이나 UI)는 스냅샷을 안전하게 조회하도록 하면 된다. 즉, 방어적 복사(불변 컬렉션/깊은 복사)와 의도에 맞는 반환 형태를 쓰면 getter는 적절히 사용 가능한 요소가 된다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;그래서 다음부터는 위 포스팅에서 제시했던 대로 다음과 같은 방식으로 해결할 예정이다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-pm-slice=&quot;0 0 []&quot;&gt;조회가 목적인 경우라면
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-pm-slice=&quot;0 0 []&quot;&gt;컬렉션은 List.copyOf / toUnmodifiableList 등 불변으로 반환&lt;/li&gt;
&lt;li data-pm-slice=&quot;0 0 []&quot;&gt;내부 변경 우려가 있으면 깊은 복사를 통해 DTO 등의 스냅샷으로 반환&lt;/li&gt;
&lt;li data-pm-slice=&quot;0 0 []&quot;&gt;단순 순회라면 Iterator/Stream 같은 최소 표면적 제공&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-pm-slice=&quot;0 0 []&quot;&gt;출력 경로 : 프린터는 도메인 스냅샷을 입력받아 출력만 담당하도록 구현(도메인은 값을 반환하고, 출력은 표현만 하면 되는 구조)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;결국 무조건 적인 getter의 회피 보다는, 도메인은 규칙 안에서 해결하고, 조회는 안전하게 할 수 있도록 활용해보는 시도를 해보려고 한다.&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #f89009;&quot;&gt;7. 컨트롤러에 대한 고민&amp;nbsp;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어쩌면 지난 1주차 과제 리뷰 중 가장 크게 와닿았던 부분은 컨트롤러의 역할을 제대로 이해하지 못하고 컨트롤러 답게 쓰지 못하고 있다는 부분이 아닐까 싶다. 아무래도 순수 자바 환경에서 컨트롤러를 구현하는 것도 처음이었고 MVC 자체에 대한 이해도가 부족한 상태에서 그냥 쓰니까 써오다보니 컨트롤러의 역할에 대해 깊이 고민해보지 못했던 것이 문제였다.&lt;br /&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;798&quot; data-origin-height=&quot;223&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dJuOmn/dJMb995JgRN/84MARbaXp0k1Ku37GXa1j1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dJuOmn/dJMb995JgRN/84MARbaXp0k1Ku37GXa1j1/img.png&quot; data-alt=&quot;1주차 피드백 내용&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dJuOmn/dJMb995JgRN/84MARbaXp0k1Ku37GXa1j1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdJuOmn%2FdJMb995JgRN%2F84MARbaXp0k1Ku37GXa1j1%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;569&quot; height=&quot;159&quot; data-origin-width=&quot;798&quot; data-origin-height=&quot;223&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;1주차 피드백 내용&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;814&quot; data-origin-height=&quot;247&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yHkYD/dJMcadG3Jlh/RywoeYXiGdQztjkTQKX9H1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yHkYD/dJMcadG3Jlh/RywoeYXiGdQztjkTQKX9H1/img.png&quot; data-alt=&quot;1주차 피드백 내용2&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yHkYD/dJMcadG3Jlh/RywoeYXiGdQztjkTQKX9H1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyHkYD%2FdJMcadG3Jlh%2FRywoeYXiGdQztjkTQKX9H1%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;580&quot; height=&quot;176&quot; data-origin-width=&quot;814&quot; data-origin-height=&quot;247&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;1주차 피드백 내용2&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 2주차에서는 좀 더 컨트롤러의 책임을 덜기 위해 어떻게 책임을 분배하면 좋을지를 고민하면서 코드를 작성해보았다.&lt;br /&gt;되도록 실행을 위한 메서드만 남겨두고 나머지 책임은 다른 클래스에 분배했다. 그럼에도 아직은 그 답을 찾지 못한 것 같다. 특히 컨트롤러의 책임을 덜기 위해 선택한 방법 중, 분할 해둔 클래스들의 인스턴스 생성을 Application 단에서 처리하도록 구현을 했었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;418&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buU7b2/dJMcahJsABA/dbqglZeh2tKK25abGRtCFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buU7b2/dJMcahJsABA/dbqglZeh2tKK25abGRtCFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buU7b2/dJMcahJsABA/dbqglZeh2tKK25abGRtCFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbuU7b2%2FdJMcahJsABA%2FdbqglZeh2tKK25abGRtCFk%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;346&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;418&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러다보니 위와 같이 Application 단에서 처리해야 할 일이 상당히 많아진 형태의 코드가 되었던 점이 마지막 제출까지도 아쉬웠다. 하지만 해결책을 달리 찾지 못하여 아쉬움을 삼키며 제출을 하였고, 이후 리뷰기간동안 다른 사람들의 코드를 참고하던 중에 꽤 괜찮은 접근법을 발견했다.&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;808&quot; data-origin-height=&quot;437&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JGj3V/dJMcaaDzdGV/EfbzNoJpbdQLtr1iVgrjWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JGj3V/dJMcaaDzdGV/EfbzNoJpbdQLtr1iVgrjWk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JGj3V/dJMcaaDzdGV/EfbzNoJpbdQLtr1iVgrjWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJGj3V%2FdJMcaaDzdGV%2FEfbzNoJpbdQLtr1iVgrjWk%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;684&quot; height=&quot;370&quot; data-origin-width=&quot;808&quot; data-origin-height=&quot;437&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 참가자분의 코드에서 리뷰를 통해 질의응답을 하고 객체를 생성하는 역할을 Config 클래스에게 맡기고 컨트롤러는 이 Config만 받아오도록 하면 &lt;b&gt;컨트롤러는 오케스트레이션에만 집중할 수 있다는 점이 매력적으로 느껴져서 다음 주차에는 적용해보려고 한다.&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;그러던 중 아니나 다를까 이번 2주차에서 컨트롤러에 대한 리뷰들도 몇개 달리게 되었는데,&amp;nbsp;&lt;br /&gt;&lt;b&gt;이전 주차보다는 컨트롤러가 훨씬 개선되었다 그러나 아직 책임이 남아있는 부분들에 대해서는 분리하기 위한 방법을 고민해봐야겠다는 내용&lt;/b&gt;이었다. 나 역시 이에 공감하며 &lt;b&gt;앞으로도 계속 컨트롤러의 책임과 역할에 대해 고민해봐야 겠다고 생각하고 있다.&lt;/b&gt;&amp;nbsp;&lt;br /&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;804&quot; data-origin-height=&quot;464&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XP2t8/dJMcaelFiNK/V6j0g26F84Zb80XQvm0dA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XP2t8/dJMcaelFiNK/V6j0g26F84Zb80XQvm0dA0/img.png&quot; data-alt=&quot;2주차 리뷰 중 컨트롤러에 대한 언급1&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XP2t8/dJMcaelFiNK/V6j0g26F84Zb80XQvm0dA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXP2t8%2FdJMcaelFiNK%2FV6j0g26F84Zb80XQvm0dA0%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;532&quot; height=&quot;307&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;464&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;2주차 리뷰 중 컨트롤러에 대한 언급1&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;807&quot; data-origin-height=&quot;313&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cSOGaj/dJMcain36cX/23oicCUDZfJB2H23zKCv60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cSOGaj/dJMcain36cX/23oicCUDZfJB2H23zKCv60/img.png&quot; data-alt=&quot;2주차 리뷰 중 컨트롤러에 대한 언급2&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cSOGaj/dJMcain36cX/23oicCUDZfJB2H23zKCv60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcSOGaj%2FdJMcain36cX%2F23oicCUDZfJB2H23zKCv60%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;621&quot; height=&quot;241&quot; data-origin-width=&quot;807&quot; data-origin-height=&quot;313&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;2주차 리뷰 중 컨트롤러에 대한 언급2&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;883&quot; data-origin-height=&quot;296&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCFgmL/dJMcagX5tyJ/zwnOTZCxee9hkNFyUlDs01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCFgmL/dJMcagX5tyJ/zwnOTZCxee9hkNFyUlDs01/img.png&quot; data-alt=&quot;2주차 리뷰 중 컨트롤러에 대한 언급3&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCFgmL/dJMcagX5tyJ/zwnOTZCxee9hkNFyUlDs01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCFgmL%2FdJMcagX5tyJ%2FzwnOTZCxee9hkNFyUlDs01%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;711&quot; height=&quot;238&quot; data-origin-width=&quot;883&quot; data-origin-height=&quot;296&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;2주차 리뷰 중 컨트롤러에 대한 언급3&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f3c000;&quot;&gt;다음 주차에 도전하고 싶은 부분&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 고민에 대해 나열하면서 다음에는 어떤 부분을 신경쓸지도 적었지만 한 번 더 정리해보자면,&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;인터페이스 사용 시 목표를 정확히 하고 사용하기 적합한 상황인지 고민해보기&lt;/li&gt;
&lt;li&gt;컨트롤러의 책임 분산을 위한 방법 더 고민해보기&lt;/li&gt;
&lt;li&gt;getter의 옳바른 사용 방법을 계속 시도해보기&lt;/li&gt;
&lt;li&gt;팩토리 패턴 사용 시 도메인 클래스들의 접근 제어 고려하기&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정도가 될 것 같다. 그리고 &lt;b&gt;2주차 공통 피드백과, 3주차 학습목표 등에 대한 도전&lt;/b&gt;만 곁들여도 충분히 고려할게 넘쳐날 것 같다...&lt;br /&gt;테스트 코드 역시 아직 다양한 JUnit, AssertJ에 대한 메서드에 대한 활용이 부족했던 것 같아서 다양한 테스트 케이스를 작성해보고 싶고, 내가 도입을 할지 말지 고민했던 '상수화'도 공통 피드백으로 권장되었기 때문에 다음부터는 우선 고민없이 적용해보고 그 이점에 대해서 정리해볼까 생각한다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;사실 1주차에 도전해보고싶다고 했던 부분들 중 일부는 적용해보았지만 그렇지 못한 부분도 있는데 이건 시간이 좀 더 걸릴 것 같다.. 적용하고 고려해야 할 사항이 점점 불어나고 있기 때문에  &lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f3c000;&quot;&gt;이번 주차 소감&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디스코드의 대화나 리뷰에서 소통을 하는 분들을 보면 수준 높은 참가자 분들이 정말 많이 보였다. 첫 주차에서는 이에 주눅들기도 하였지만 이번 2주차 과제를 진행하는 동안 &lt;b&gt;1주차의 나보다 훨씬 성장한 점들을 느끼면서 더는 크게 의식하지 않게 되었다.&lt;/b&gt; 어제의 나보다 성장하는 것을 목표로 나에게 필요한 것들을 배우고 적용하면서 무사히 프리코스를 완주한다면 큰 성장을 할 수 있을 것이라는 확신이 들었다.&lt;br /&gt;소감문을 작성할 때까지만 해도, 그리고 이 회고글에도 적었지만 &lt;b&gt;'설계에 너무 과한 시간을 투자한게 아닌가 결국 고치고 완성해 나가는 방향이 옳았을 것 같다'&lt;/b&gt;는 생각을 가지고 작성 했었는데, 리뷰를 진행하는 과정에서 내가 설계를 고민한 끝에 선택한 팩토리 패턴에 대한 칭찬을 많이 받게 되어 내가 고민한 시간이 헛되지 않았음을 느끼고 너무 큰 성취감을 느꼈다. 최근 취업, 개발, 개인 공부를 돌이켜보면 좌절의 연속이라 내가 하는 무엇 하나도 제대로 해내는게 없는 것 같다는 생각이 자주 들었는데 얼마 만에 내가 고민한 코드가 인정 받은 것인지..&lt;br /&gt;&amp;nbsp;&lt;br /&gt;내 스스로의 코드 설계 능력에 자신이 없어서 어떻게든 셀프 피드백을 하고 방향성을 점검하고자 하나씩 작성했던 클래스 다이어그램도 다른 참가자 분이 인상깊게 보았다는 내용들을 보고 틀리지 않았구나 생각하게 되었다. 사실 지금도 UML 클래스 다이어그램을 작성하는 규칙이 무엇인지 제대로 알지 못한다... 그냥 생각을 정리할 도구가 필요해서 기억나는 방법대로 그려봤을 뿐이었는데 이 보잘 것 없는 그림 쪼가리가 내 코드를 읽는데 도움이 되었다고 하니 앞으로도 꾸준히 그려나갈 생각이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;776&quot; data-origin-height=&quot;118&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QkZlC/dJMcadNPmyQ/dmgvTM6qQEJkR0CEYqDrwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QkZlC/dJMcadNPmyQ/dmgvTM6qQEJkR0CEYqDrwk/img.png&quot; data-alt=&quot;칭찬 감사합니다  &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QkZlC/dJMcadNPmyQ/dmgvTM6qQEJkR0CEYqDrwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQkZlC%2FdJMcadNPmyQ%2FdmgvTM6qQEJkR0CEYqDrwk%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;566&quot; height=&quot;86&quot; data-origin-width=&quot;776&quot; data-origin-height=&quot;118&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;칭찬 감사합니다  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>My Story/우아한테크코스</category>
      <category>우아한테크코스8기</category>
      <category>프리코스</category>
      <category>회고</category>
      <author>_루디_</author>
      <guid isPermaLink="true">https://kimjoraeng.tistory.com/85</guid>
      <comments>https://kimjoraeng.tistory.com/85#entry85comment</comments>
      <pubDate>Thu, 30 Oct 2025 00:32:47 +0900</pubDate>
    </item>
    <item>
      <title>[회고] 우아한 테크코스 백엔드 8기 프리코스 1주차 - 문자열 계산기</title>
      <link>https://kimjoraeng.tistory.com/80</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;473&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7xGO6/dJMb9LKKjLC/BKAjO19vYH6K9f31N5ZcmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7xGO6/dJMb9LKKjLC/BKAjO19vYH6K9f31N5ZcmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7xGO6/dJMb9LKKjLC/BKAjO19vYH6K9f31N5ZcmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7xGO6%2FdJMb9LKKjLC%2FBKAjO19vYH6K9f31N5ZcmK%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;1280&quot; height=&quot;473&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;473&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 1주차가 다 끝나지 않았지만, 인턴 출퇴근을 병행하고 있기 때문에 내일이 되면 또 시간이 부족할 것 같아 미리 작성하려고 한다.&lt;br /&gt;우테코 프리코스를 시작하게 되고 1주차 동안 어떻게 공부를 했었는지 되돌아보자&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프로젝트 개요&lt;/h2&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;ex) &quot;&quot; =&amp;gt; 0, &quot;1,2&quot; =&amp;gt; 3, &quot;1,2,3&quot; =&amp;gt; 6, &quot;1,2:3&quot; =&amp;gt; 6&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;기본 구분자(쉼표, 콜론) 외에 커스텀 구분자를 지정할 수 있다. 커스텀 구분자는 문자열 앞부분의 &quot;//&quot;와 &quot;\n&quot; 사이에 위치하는 문자를 커스텀 구분자로 사용한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ex) &quot;//;\n1;2;3&quot;과 같이 값을 입력할 경우 커스텀 구분자는 세미콜론(;)이며, 결과 값은 6이 반환되어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;사용자가 잘못된 값을 입력할 경우 IllegalArgumentException을 발생시킨 후 애플리케이션은 종료되어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;구현 기능 목록&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;문자열 입력기&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 사용자에게 안내 문자열을 출력&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 사용자로부터 문자열을 입력받기
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;과제 라이브러리 가이드라인을 따라 &lt;code&gt;Console&lt;/code&gt; 클래스와 &lt;code&gt;readLine()&lt;/code&gt;메서드 사용문자열 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 입력받은 문자열을 저장하여 관리구분자 관리&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 구분자 값을 저장하여 관리문자열 검사기&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 입력받은 문자열을 복제하여 관리&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 문자열에 커스텀 구분자 문법이 있는 경우 뒤쪽 문자열과 분리 커스텀 구분자는 구분자 값 리스트에 추가하는 기능&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 가장 먼저 나온 구분자 위치 반환하는 기능숫자 분리기&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 구분자 값을 기준으로 문자열의 숫자들을 분리하여 저장계산기&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 분리된 숫자들을 총합하여 계산출력기&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 최종 계산 결과를 받아서 출력예외처리&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 커스텀 구분자로 한글자 이상을 입력한 경우&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 사용자가 범위를 벗어난 값을 입력한 경우&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 기본 구분자도, 특수문자도, 숫자도 아닌 값을 입력받는 경우 예외처리&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 구분자가 연속해서 오는 경우, 혹은 선행 연산자로 인해 빈 문자열을 토큰으로 리턴하는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;리팩토링 순서&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 컨트롤러 책임 분리
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 숫자 리스트 도메인 및 합계 계산 분리&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 구분자 기준 문자열 분할 분리&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 문자열 -&amp;gt; 숫자 파싱 분리&lt;/li&gt;
&lt;li&gt;&lt;input checked=&quot;checked&quot; disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&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;h3 data-ke-size=&quot;size23&quot;&gt;전체적인 시스템 흐름도&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1245&quot; data-origin-height=&quot;683&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p228s/dJMb85I9MtW/6YoSJaDbOn9cDp3muklvb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p228s/dJMb85I9MtW/6YoSJaDbOn9cDp3muklvb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p228s/dJMb85I9MtW/6YoSJaDbOn9cDp3muklvb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp228s%2FdJMb85I9MtW%2F6YoSJaDbOn9cDp3muklvb0%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;1245&quot; height=&quot;683&quot; data-origin-width=&quot;1245&quot; data-origin-height=&quot;683&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  이번에 고민한 내용들&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;미션에 어떤 내용까지 적용해야할까&lt;/h3&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;p data-ke-size=&quot;size16&quot;&gt;그래서 내 목표를 다시 점검해보기로 했다. 내가 모르던 내용들을 교류를 통해 배우는 것, 사람들과 깊이 있는 고민을 주고받고 성장하는 것, 양질의 리뷰를 주고받는 경험을 하는 것, 아직 미숙한 객체지향적 사고를 체화하는 것, 자바 숙련도를 높이는 것 등 많지만 역시 &lt;b&gt;0순위 목표는 프리코스 합격&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 합격을 최우선으로 하기 위해서 우테코가 지원자들에게 바라는 역량이 무엇인지, 우테코가 어떤 방향성을 가지고 있는지 다시 체크할 필요가 있었다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;우테코의 인재상&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;706&quot; data-origin-height=&quot;338&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/y5wSd/dJMb9WyE3gp/NlsoNZ9KdyhaUApLNlxgKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/y5wSd/dJMb9WyE3gp/NlsoNZ9KdyhaUApLNlxgKK/img.png&quot; data-alt=&quot;2024 우테코 모집글 : https://techblog.woowahan.com/14072/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/y5wSd/dJMb9WyE3gp/NlsoNZ9KdyhaUApLNlxgKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fy5wSd%2FdJMb9WyE3gp%2FNlsoNZ9KdyhaUApLNlxgKK%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;706&quot; height=&quot;338&quot; data-origin-width=&quot;706&quot; data-origin-height=&quot;338&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;2024 우테코 모집글 : https://techblog.woowahan.com/14072/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인재상을 읽어보면 저런 사람이 되는 것을 목표로 해야겠다는 생각은 들어도 어떻게 하면 저렇게 될 수 있을지 방법은 아직 와닿지 않았다. 그러던 중 우테코 소개 페이지에 적힌 캡틴들의 메시지를 읽고 고민이 해결되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;캡틴의 메시지&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&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;/blockquote&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;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;921&quot; data-origin-height=&quot;511&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qzCHF/dJMb9Xj19dd/nY0OdheNrBjF2mG7Rtw4w1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qzCHF/dJMb9Xj19dd/nY0OdheNrBjF2mG7Rtw4w1/img.png&quot; data-alt=&quot;(입학설명회 설명 내용 중에서)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qzCHF/dJMb9Xj19dd/nY0OdheNrBjF2mG7Rtw4w1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqzCHF%2FdJMb9Xj19dd%2FnY0OdheNrBjF2mG7Rtw4w1%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;588&quot; height=&quot;326&quot; data-origin-width=&quot;921&quot; data-origin-height=&quot;511&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;(입학설명회 설명 내용 중에서)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몰입을 할땐 하더라도 &quot;아무튼 중요하니까 덮어두고 공부하자!&quot; 보다는 &lt;b&gt;의문을 가지고 지금 배우려고 하는 내용이 왜 중요한지, 어떤 경우에 좋은건지, 지금 나에게 이것이 중요한게 맞는지 고민해보는 시간을 가지면서&lt;/b&gt; 몰입을 해야한다. 이 사실을 계속 의식하면서 고민이 생길 때마다 다음의 질문들을 적용하기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;내가 정한 방향성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 1주차 미션은 난이도 자체가 높지 않아서 완성을 목표로 하라고 하면 하루만에 끝낼 수도 있었다. 하지만 &lt;b&gt;목표는 제출이 아니라 성장이었기 때문에 이걸 통해 내가 무엇을 얻어갈 수 있을지 생각하면서&lt;/b&gt; 모든 요구사항과 규칙, 스타일 가이드 등에 대해 의미를 부여하고 무슨 의도로 작성된 것인지 생각하면서 설계하고 코드를 작성하기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 작성하다 고민이 생기는 부분들은 전부 리스트로 적어서 정리했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;731&quot; data-origin-height=&quot;367&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bN066Z/dJMb8YwuYqT/ooXJDsEBKJSLGP0dSyEoTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bN066Z/dJMb8YwuYqT/ooXJDsEBKJSLGP0dSyEoTk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bN066Z/dJMb8YwuYqT/ooXJDsEBKJSLGP0dSyEoTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbN066Z%2FdJMb8YwuYqT%2FooXJDsEBKJSLGP0dSyEoTk%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;593&quot; height=&quot;367&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;731&quot; data-origin-height=&quot;367&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;191&quot; data-origin-height=&quot;568&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DdXJN/dJMb9PM9C3J/R4nZQtCwnbA4SJizCkRtk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DdXJN/dJMb9PM9C3J/R4nZQtCwnbA4SJizCkRtk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DdXJN/dJMb9PM9C3J/R4nZQtCwnbA4SJizCkRtk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDdXJN%2FdJMb9PM9C3J%2FR4nZQtCwnbA4SJizCkRtk1%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;191&quot; height=&quot;568&quot; data-origin-width=&quot;191&quot; data-origin-height=&quot;568&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 위 리스트의 내용 처럼 처음에는 구현하려는 기능을 최대한 잘게 쪼개면서 구현하면 될거라고 생각했는데 기준 없이 쪼개다보니 작게 쪼갠다고 좋은게 맞는가에 대한 의문이 생겼다. 객체지향의 근본적인 목표를 잊고 무작정 잘게 쪼개기만 하다보니 &lt;b&gt;무엇을 위해 쪼개는 것인지, 어느 기준에 맞춰 쪼개야 하는지&lt;/b&gt;를 잊고 쪼개는 행위 자체에만 초점을 맞추게 되어서 생긴 의문들이었다. 이 고민들을 해결하는 과정에서 자연스럽게 &lt;b&gt;객체지향의 모듈화 개념과 응집성&amp;bull;결집성, SOLID&lt;/b&gt;에 대해 조사하게 되었다. 회고에서 다루기엔 양이 많아서 포스팅은 따로 작성했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;자바 스타일 가이드와 클린코드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 협업을 하면서 어느정도 컨벤션을 정하긴 했었지만 그래봤자 변수명 짓기와 들여쓰기 칸 수 정도에 그쳤다. 자바스크립트 기준 언제 익명함수와 arrow function, 콜백을 허용하는지 정도를 의식했는데 우테코에서 권장하는 자바 스타일 가이드는 구글 스타일 가이드를 기반으로 굉장히 디테일한 제약사항들을 정해두었다. 또한 이전 기수 참가자들의 회고를 찾아보다가 자바 스타일 가이드 뿐 아니라 &lt;b&gt;우테코의 PR체크리스트&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;궁금증이 어느정도 해결되고 적용만 잘하면 되겠다고 생각은 했는데 이 많은 규칙들을 모두 준수하면서 작성하는게 가능할지 코드를 쓰다보면 잊게될 것 같아 걱정했는데 우테코 정보 수집을 위해 블로그들을 찾아다니던 중 다행히 &lt;a href=&quot;https://velog.io/@sun007021/IntelliJ-%EC%BD%94%EB%93%9C-%EC%BB%A8%EB%B2%A4%EC%85%98-%ED%81%B4%EB%A6%B0%EC%BD%94%EB%93%9C-%EA%B2%80%EC%82%AC-%EC%9E%90%EB%8F%99-%EC%84%A4%EC%A0%95-%EA%B0%80%EC%9D%B4%EB%93%9C&quot;&gt;이 포스팅을 발견하고&lt;/a&gt; IDE에 스타일을 적용하고 큰 어려움 없이 스타일 가이드를 준수하며 코드를 작성할 수 있었다.&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;객체지향적인 설계로 책임 분리하기(feat. 객체지향체조, MVC)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;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;1주차 미션에서는 이를 적극적으로 적용하면서 객체지향을 준수하는 코드를 작성해보기로 했다. 그러기 위해 요구사항을 잘게 쪼개서 기능들을 정의하고 순서대로 구현하면서 객체지향을 적용하려 했으나 지금껏 해온 방식과 너무 달라서 진도가 안나갔다. 분명 이론은 배운적 있으나 정의를 달달 외웠을 뿐 지금껏 객체지향의 원칙들이 생긴 이유와 그 내용을 어디에 어떻게 적용해야 하는지 의문을 가지고 써본적은 없었으니 당연했다. 방법도 모르고 우테코에서 지정했던 PR체크리스트(객체지향체조 9가지)를 맹목적으로 피해야 한다고 의식하면서 코드를 쓰다 보니 하나의 원칙을 준수하면 다른 원칙을 어기는 상황이 반복되어 진도가 막혔던 것이다. 하면 안되는 것은 인지했는데 그걸 준수하기 위해 어떻게 해야하는지는 모르는 그런 막막한 상황이었다(...)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 우선 객체지향을 무시하고 &lt;b&gt;원래 하던 방식대로 기능 우선으로 작성을 하고, 그 다음 규칙에 맞게 코드를 고치면서 개선시켜보기로&lt;/b&gt; 했다.&lt;br /&gt;아래 내용이 처음 완성한 기능의 클래스 다이어그램이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;743&quot; data-origin-height=&quot;424&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNJrrs/dJMb9NPkOSr/j0umCYOKfLU9Kd93LvWop1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNJrrs/dJMb9NPkOSr/j0umCYOKfLU9Kd93LvWop1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNJrrs/dJMb9NPkOSr/j0umCYOKfLU9Kd93LvWop1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNJrrs%2FdJMb9NPkOSr%2Fj0umCYOKfLU9Kd93LvWop1%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;611&quot; height=&quot;349&quot; data-origin-width=&quot;743&quot; data-origin-height=&quot;424&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;책임에 대한 분리도 안되어있고, 기본적인 은닉화, 캡슐화 등의 조건이 하나도 이루어져있지 않다. 자바 위에서 작성을 했으니 클래스를 꾸역꾸역 썼을 뿐 전혀 클래스의 기능을 활용하고 있지 못한 상태였다.&lt;br /&gt;객체지향의 원칙 9가지가 강조하는 내용은 응집성, 결집성에 대한 내용과 단일책임원칙을 준수하기 위함이 핵심이라고 판단했고 각 기능들이 어떤 책임을 지고 있는지를 따져봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;692&quot; data-origin-height=&quot;385&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/N53U5/dJMb9XRSlWD/XhnJtc1AuOteDCbqvOD9KK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/N53U5/dJMb9XRSlWD/XhnJtc1AuOteDCbqvOD9KK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/N53U5/dJMb9XRSlWD/XhnJtc1AuOteDCbqvOD9KK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FN53U5%2FdJMb9XRSlWD%2FXhnJtc1AuOteDCbqvOD9KK%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;627&quot; height=&quot;349&quot; data-origin-width=&quot;692&quot; data-origin-height=&quot;385&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열과 정수 리스트 데이터를 컨트롤러에서 다루고 있었으며, 그에 대한 계산도 컨트롤러에서 하고 있었다. 심지어 splitStringToNumber의 경우 문자열 분리, 문자열의 숫자로 파싱, 문자열과 숫자리스트 값의 갱신을 모두 혼자 맡고있었다. 마지막으로 SeparatorDetector에서는 커스텀 구분자에 대한 추가와 함께 전체 문자열에서 구분자가 처음 나오는 위치의 인덱스를 반환하는 역할도 하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class Controller {
    private final Output output = new Output();
    private String inputString;
    private Seperators seperators;
    private ArrayList&amp;lt;Integer&amp;gt; numberList = new ArrayList&amp;lt;&amp;gt;();
    ...

     private void splitStringToNumber(String str, int seperatorIdx) {
        String numberToken = str.substring(0, seperatorIdx);

        try {
            int number = Integer.parseInt(numberToken);

            if (number &amp;lt; 0) { // validaton 분리
                throw new IllegalArgumentException(
                        &quot;음수값은 허용되지 않습니다.: &quot; + numberToken
                );
            }
            String remainString = str.substring(seperatorIdx + 1);
            numberList.add(number);  // 앞쪽값은 numberList.add
            this.inputString = remainString; // 뒤쪽값을 새 문자열로 갱신
         } catch (NumberFormatException e) {
            throw new IllegalArgumentException(
                    &quot;정수 범위를 초과했거나 유효하지 않은 값이 포함되어 있습니다.: &quot; + numberToken
            );
        }
    }
     private int calculateNumber(ArrayList&amp;lt;Integer&amp;gt; numberList){
        int sum = 0;
        for (Integer integer : numberList) {
            sum += integer;
        }
        return sum;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 컨트롤러에 대부분의 책임이 몰려있는 것은, 처음에 분리해서 만드는 것을 시도한 적은 있었으나 객체지향체조의 get/set을 쓰지 말라는 규칙에서 방법을 찾지 못해 결국 우선 컨트롤러에 몰아넣게 되었다. 분할을 해놨는데 접근해서 사용은 못한다면 어떻게 구현해야 할지 떠오르지 않았다.&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;이때는 get/set을 절대 쓰면안된다 정도로 의식하고 아예 값을 건드리면 안되면 어떻게 의존을 하지 고민하고 있었는데 알고보니 get/set을 쓰지 말라는 규칙은 &lt;b&gt;'필드를 외부에 노출하거나 외부에서 임의로 바꾸게 하지 말라'는 뜻&lt;/b&gt;이며, &lt;b&gt;상태 변경은 set함수가 아니라 도메인 메서드를 통해 허용&lt;/b&gt;하면 된다. &lt;b&gt;조회가 필요할 땐 필요한 값만 의도를 드러내는 조회용 메서드(값의 수정이 불가능한)를 제공하면 된다&lt;/b&gt;는 것을 공부하면서 알게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 여러 수정을 걸쳐 &lt;b&gt;&lt;code&gt;Controller&lt;/code&gt;의 &lt;code&gt;numberList&lt;/code&gt;와 &lt;code&gt;calculateNumber&lt;/code&gt;를 하나의 &lt;code&gt;Numbers&lt;/code&gt; 클래스로&lt;/b&gt; 분리, &lt;b&gt;Controller의 &lt;code&gt;InputString&lt;/code&gt;과 &lt;code&gt;splitStringToNumber&lt;/code&gt;의 문자열 분리, &lt;code&gt;SeparatorDetector&lt;/code&gt;의 커스텀 구분자 인덱스 찾는 기능을 분리하여 &lt;code&gt;InputCursor&lt;/code&gt; 클래스로 분리&lt;/b&gt;하고 마지막으로 &lt;b&gt;&lt;code&gt;splitStringToNumber&lt;/code&gt;에서 문자열을 숫자로 파싱하는 기능은 다시 &lt;code&gt;TokenParser&lt;/code&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;또 처음에는 한 패키지에 몰아서 클래스를 정리했으나 클래스 간에 역할을 구분짓기가 쉽지 않고 헷갈려서 정리하는 방법을 찾다가 MVC패턴을 적용하여 분리하는 것을 시도해 보았다. 항상 백엔드 프로젝트를 진행하면서 컨트롤러 - 서비스 - 도메인 - DB 구조의 아키텍처가 유명하고 다들 그렇게 만들고 있었기 때문에 별다른 의심 없이 맹목적으로 따라서 만들었는데 순수 자바에서도 구현이 가능하다는 것을 알고 나서 &lt;b&gt;실제 MVC 각 계층의 역할이 무엇인지&lt;/b&gt; 처음으로 이해하고 미션에 적용할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1126&quot; data-origin-height=&quot;568&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Utwq4/dJMb9OnaIzz/W8lxjyKnmjYMCHtYS77O7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Utwq4/dJMb9OnaIzz/W8lxjyKnmjYMCHtYS77O7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Utwq4/dJMb9OnaIzz/W8lxjyKnmjYMCHtYS77O7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUtwq4%2FdJMb9OnaIzz%2FW8lxjyKnmjYMCHtYS77O7K%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;747&quot; height=&quot;377&quot; data-origin-width=&quot;1126&quot; data-origin-height=&quot;568&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들어, 나는 View라는 계층을 단순히 페이지를 렌더링하는 계층이라고 생각하고 있었다. 하지만 &lt;b&gt;사용자와 I/O를 통한 커뮤니케이션을 담당하는 계층&lt;/b&gt;이라는 것을 알게 되었고, 기존에는 컨트롤러나 메인 클래스에서 입출력 처리를 생각하고 있었는데 뷰 클래스로 Input Output을 만들어 컨트롤러에서 직접적인 입출력 역할을 수행하지 않도록 분리하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;689&quot; data-origin-height=&quot;127&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uWxCp/dJMb9VNh183/fHRygy4HhVjfOo6KWTmqr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uWxCp/dJMb9VNh183/fHRygy4HhVjfOo6KWTmqr0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uWxCp/dJMb9VNh183/fHRygy4HhVjfOo6KWTmqr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuWxCp%2FdJMb9VNh183%2FfHRygy4HhVjfOo6KWTmqr0%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;635&quot; height=&quot;117&quot; data-origin-width=&quot;689&quot; data-origin-height=&quot;127&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;605&quot; data-origin-height=&quot;186&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cRWDm9/dJMb9fLMbLp/wPuE83QTzZtabjWfljv3E1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cRWDm9/dJMb9fLMbLp/wPuE83QTzZtabjWfljv3E1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cRWDm9/dJMb9fLMbLp/wPuE83QTzZtabjWfljv3E1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcRWDm9%2FdJMb9fLMbLp%2FwPuE83QTzZtabjWfljv3E1%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;605&quot; height=&quot;186&quot; data-origin-width=&quot;605&quot; data-origin-height=&quot;186&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨트롤러는 본래 이해하던 역할과 크게 차이점은 없었지만, 모델의 경우 단순히 데이터를 저장하는 역할 이라고만 생각하고 있었다. 실제로는 &lt;b&gt;그 데이터를 처리하는 로직(비즈니스 규칙)을 모두 포함하고 모델 클래스에서는 그 모델이 다루는 데이터에 대한 내부 행위를 담당해야 한다&lt;/b&gt;는 것을 알게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 모델 계층에 해당하는 &lt;code&gt;Separators&lt;/code&gt;, &lt;code&gt;Numbers&lt;/code&gt;, &lt;code&gt;InputCursor&lt;/code&gt;, &lt;code&gt;TokenParser&lt;/code&gt;, &lt;code&gt;SeparatorDetector&lt;/code&gt;로 분리하는 과정에서 각 클래스 간에 책임을 명확히하고 응집도와 캡슐화를 고려하며 코드를 쓰려고 노력했다. &lt;b&gt;특히 상태의 갱신은 모델 안에서만 하는 것&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;634&quot; data-origin-height=&quot;244&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bX25jK/dJMb9Wk75yA/yxXEQPVfcNxiAVFQcG7BH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bX25jK/dJMb9Wk75yA/yxXEQPVfcNxiAVFQcG7BH0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bX25jK/dJMb9Wk75yA/yxXEQPVfcNxiAVFQcG7BH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbX25jK%2FdJMb9Wk75yA%2FyxXEQPVfcNxiAVFQcG7BH0%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;634&quot; height=&quot;244&quot; data-origin-width=&quot;634&quot; data-origin-height=&quot;244&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;325&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cVeCTk/dJMb9WFqNW9/4CDcuHD8D3mGKijuBJ3RQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cVeCTk/dJMb9WFqNW9/4CDcuHD8D3mGKijuBJ3RQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cVeCTk/dJMb9WFqNW9/4CDcuHD8D3mGKijuBJ3RQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcVeCTk%2FdJMb9WFqNW9%2F4CDcuHD8D3mGKijuBJ3RQK%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;301&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;325&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Validation과 도메인 로직의 경계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스들을 만들고 분리하는 과정에서 그리고 중첩되는 if들을 제거하는 과정에서 자연스럽게 validation의 필요성을 느끼게 되었다. 그런데 validator를 만들어서 분리할 때 그 기준을 어떻게 잡아야 할지 고민이 되었다.&lt;/p&gt;
&lt;pre id=&quot;code_1760973164936&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; public int parseInteger(String token) {
        int parsedInt;
        try {
            parsedInt = Integer.parseInt(token);
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException(
                    &quot;범위를 초과했거나 유효하지 않은 값이 포함되어 있습니다 :&quot; + token
            );
        }
        if (parsedInt &amp;lt; 0) {
            throw new IllegalArgumentException(
                    &quot;음수 값은 허용되지 않습니다 : &quot; + token
            );
        }
        return parsedInt;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 상황과 같이 단순히 값을 검증하고 예외처리를 하는 경우는 Validatior으로 분리하였으나&lt;/p&gt;
&lt;pre id=&quot;code_1760973386419&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; public String cutBefore(List&amp;lt;String&amp;gt; separator) {
        // seperator가 1글자인지 체크하는 validation 추가
        int firstSepIdx = Integer.MAX_VALUE;
        String firstSeperator = null;
        for (String s : separator) {
            int i = restString.indexOf(s);
            if (i != -1 &amp;amp;&amp;amp; i &amp;lt; min) {
                firstSepIdx = i;
                firstSeperator = s;
            }
        }
        
        ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 if문이 특정 값을 변경하고 있는 경우는 상태를 변경하고 있는 경우에는 state를 관리하는 임시 객체(Value Object)를 만들고 함수로 해당 if문을 분리하여 2depth문제를 해결하였다.&lt;/p&gt;
&lt;pre id=&quot;code_1760973996817&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private static class SeperatorState { // 상태 관리용 임시 객체
        int firstSepIdx = Integer.MAX_VALUE;
        String firstSeperator = null;
    }
    ...
    
 private void updateSeperatorState(int index, String seperator, SeperatorState state) {
        if (index != -1 &amp;amp;&amp;amp; index &amp;lt; state.firstSepIdx) {
            state.firstSepIdx = index;
            state.firstSeperator = seperator;
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 외에도 입력 형식(ex. null, 공백) 등은 입력단(Controller)에서, 단순 가드 역할을 하는 (ex. 비어있는 문자열, 토큰 등) 경우는 로컬 if문으로 처리하도록 Validation의 역할을 명확히 했다. 결국 if문을 통한 검증을 모두 분리할게 아니라 응집도와 책임을 기준으로 그 위치를 정하는게 중요한 것 같다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이번에 새롭게 알게된 내용  &lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;패키지 분리 후 접근 제어&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MVC 기준으로 패키지를 분리하기 전에는 문제가 없었는데, 패키지를 분리하고 난 이후에 Numbers 클래스를 불러오지 못하는 문제가 발생했었다. 내가 Number &lt;b&gt;클래스 생성자를 작성하는 과정에서 접근제어자를 까먹고 작성하지 않은 것&lt;/b&gt;인데, 이게 같은 패키지 내에 있을 때에는 문제가 되지 않았다가 MVC패턴을 적용하여 &lt;b&gt;패키지가 분리되고 나니 생성자가 public이 아니라 외부에서 접근할 수 없던 거다&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;자바의 기본 생성자 접근 제어자는 package-private이라 같은 패키지 내부에서만 접근&lt;/b&gt;할 수 있었다. 아마 언젠가 읽어본 적은 있던 것 같은데 내용도 까먹고 있었고 public을 써주는 것도 까먹고 있어서 생긴 해프닝이었다.  &lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;내부 클래스와 static class 인스턴스&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트를 사용할 때에는 임시로 객체를 리터럴로 생성해서 여러 값을 리턴하거나 주고받을 때 자주 사용을 했었는데 자바에서는 이게 익숙하지 않다보니 리턴 해야 할 상태가 많아질 때 자꾸 코드를 작성하다 막히게 되었다. 찾아보니 자바에서도 이런 경우 &lt;b&gt;내부 클래스를 작성해서 이를 Value Object로 사용하는 방식&lt;/b&gt;이 있었다. 위에서 다루었던 SeparatorState를 만드는 경우도 그렇고, SeparatorDetector에서도 원래는 커스텀 구분자와, 커스텀 문법을 분리한 문자열을 동시에 리턴해야 하는 문제가 있었을 때 Result 클래스를 만들어서 해결하기도 하였다.&lt;/p&gt;
&lt;pre id=&quot;code_1760974157153&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static class Result {
        private final String custom;
        private final String body;
        public Result(String custom, String body) {
            this.custom = custom;
            this.body = body;
        }
        public String getCustom() {
            return custom;
        }
        public String getBody() {
            return body;
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트에 비해서는 좀 번거롭게 클래스를 만들어 주어야 하지만 그래도 이젠 다루는 값이 많을 때에 뇌정지가 덜 올 수 있을 것 같다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;다음 주차 목표&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 미션에서는 객체지향 사고방식에 적응하는 시간을 가지고 SOLID, 객체지향체조, MVC패턴 등에 익숙해지는 시간이었는데, 돌이켜보면 SOLID에서 집중해서 실천한 내용은 단일책임원칙(SRP) 뿐인 것 같다.&lt;br /&gt;다음 주차 미션에서는 &lt;b&gt;나머지 원칙들에 대해서도 준수할 수 있도록&lt;/b&gt; 하려고 한다.&lt;br /&gt;그리고 지금까진 기능 테스트나 디버깅을 항상 직접 값을 출력해보면서 수작업으로 진행하다가 처음 우테코 리포에 제공된 테스트 코드를 사용해봤는데 생각한 것 보다 어렵지 않고 훨씬 편하게 작업을 할 수 있었어서 관심이 생겼다. 다음 미션에서는&lt;b&gt; 테스트 코드에 대한 이론을 좀 더 학습하고 적용할 수 있는 내용이 있다면 적용&lt;/b&gt;해보려고 한다.&lt;br /&gt;그리고 객체지향체조를 공부하던 중, 원시값을 객체로 포장한 경우 검증을 해결하는 방식으로 생성자 호출, 정적 팩토리 메서드 두가지 방식이 있음을 알게되었다. 이번에는 생성자 호출 방식에 가까웠던 것 같은데 이펙티브 자바에서는 정적 팩토리 메서드를 권장한다고 하여 &lt;b&gt;다음 주차에서 검증을 구현할 때에는 정적 팩토리 방식을 적용&lt;/b&gt;해보고 비교해보려고 한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;미션 구현 PR&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/woowacourse-precourse/java-calculator-8/pull/526&quot;&gt;https://github.com/woowacourse-precourse/java-calculator-8/pull/526&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1760974292466&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;[문자열 덧셈 계산기] 김선우 미션 제출합니다. by meoraeng &amp;middot; Pull Request #526 &amp;middot; woowacourse-precourse/java-ca&quot; data-og-description=&quot;구현 내용 전체 아키텍쳐 각 클래스 단일 책임 원칙을 준수하여 설계 MVC 패턴 기준으로 클래스 역할 분리 기능 구현 커스텀 구분자 처리 : 문자열 앞에 // \n 이 있는 경우 커스텀 구분자로 취급하&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/woowacourse-precourse/java-calculator-8/pull/526&quot; data-og-url=&quot;https://github.com/woowacourse-precourse/java-calculator-8/pull/526&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/V8Cpc/hyZL48UQY6/L5DDMYhIK6grHyCFvkKGS1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/WwrSE/hyZLomcQJi/ZvSmdj80fhzuGc69cTiyZk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/woowacourse-precourse/java-calculator-8/pull/526&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/woowacourse-precourse/java-calculator-8/pull/526&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/V8Cpc/hyZL48UQY6/L5DDMYhIK6grHyCFvkKGS1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/WwrSE/hyZLomcQJi/ZvSmdj80fhzuGc69cTiyZk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;[문자열 덧셈 계산기] 김선우 미션 제출합니다. by meoraeng &amp;middot; Pull Request #526 &amp;middot; woowacourse-precourse/java-ca&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;구현 내용 전체 아키텍쳐 각 클래스 단일 책임 원칙을 준수하여 설계 MVC 패턴 기준으로 클래스 역할 분리 기능 구현 커스텀 구분자 처리 : 문자열 앞에 // \n 이 있는 경우 커스텀 구분자로 취급하&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;</description>
      <category>My Story/우아한테크코스</category>
      <category>우아한테크코스8기</category>
      <category>프리코스</category>
      <category>회고</category>
      <author>_루디_</author>
      <guid isPermaLink="true">https://kimjoraeng.tistory.com/80</guid>
      <comments>https://kimjoraeng.tistory.com/80#entry80comment</comments>
      <pubDate>Mon, 20 Oct 2025 01:02:58 +0900</pubDate>
    </item>
    <item>
      <title>[Python] 클래스, 상속</title>
      <link>https://kimjoraeng.tistory.com/79</link>
      <description>&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;본 포스팅은 학교 수업에서 다룬 파이썬 문법 부분을 정리해두고자 남기는 포스팅이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;(해당 수업은 '파이썬 도장'과 '점프 투 파이썬'을 교재로 문법 수업을 진행)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1280&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dcvthA/btsM9lKsAdw/IX2aIhndgqm7YT6WjOrzek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dcvthA/btsM9lKsAdw/IX2aIhndgqm7YT6WjOrzek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dcvthA/btsM9lKsAdw/IX2aIhndgqm7YT6WjOrzek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdcvthA%2FbtsM9lKsAdw%2FIX2aIhndgqm7YT6WjOrzek%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;309&quot; height=&quot;309&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1280&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;클래스&lt;/h2&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;br /&gt;복잡한 문제를 잘게 나누어 객체로 만들고, 객체를 조합해서 문제를 해결하는 방식&lt;br /&gt;현실 세계 문제를 처리하는데 유용하며 기능을 개선하고 발전시킬 때도 해당 클래스만 수정하면 되기 때문에 유지 보수에 효율적이다.&lt;/li&gt;
&lt;li&gt;명사화 할 수 있는 데이터를 클래스의 속성, 기능(클래스를 제어하는 동작)을 메서드 라고 부른다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;class&lt;/code&gt;로 클래스 이름을 지정하고 :(콜론)을 붙인 뒤 다음 줄부터 &lt;code&gt;def&lt;/code&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;code&gt;self&lt;/code&gt;를 지정해야 한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;class 클래스 이름: def 메서드(self): 코드&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스를 통해 객체를 생성하면 해당 객체를 인스턴스라고 부른다.&lt;br /&gt;&lt;code&gt;인스턴스 = 클래스()&lt;/code&gt;&lt;br /&gt;클래스는 특정 개념을 표현만 할 뿐 실제 사용을 하기 위해서는 인스턴스를 생성해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메서드는 클래스가 아니라 인스턴스를 통해 호출하고 이렇게 인스턴스를 통해 호출하는 메서드를 &lt;b&gt;인스턴스 메서드&lt;/b&gt;라고 부른다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;파이썬의 클래스&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 다루었던 int, list, dict 등도 클래스이다.&lt;br /&gt;이 클래스로 인스턴스를 만들고 메서드를 사용한다.&lt;br /&gt;파이썬은 모든게 객체(인스턴스)로 되어있다고 볼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;a = 10
type(a) # &amp;lt;class 'int'&amp;gt;
b = [0, 1, 2]
type(b) # &amp;lt;class 'list'&amp;gt;
c = {'x':10, 'y':20}
type(c) # &amp;lt;class 'dict'&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[[리스트&amp;amp;튜플 응용]]에서 사용한 메서드들 또한, 인스턴스인 리스트 a에서 메서드 append를 호출해서 값을 추가하는 과정 처럼 클래스 관점으로 해석 가능하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;속성 사용하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬에서 속성을 만들 때는 &lt;code&gt;__init__&lt;/code&gt; 메서드 안에서 self.속성에 값을 할당해야 한다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;class 클래스 이름:
    def __init__(self):
         self.속성 = 값&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;init 메서드는 생성자 역할을 하여, 클래스를 통해 &lt;b&gt;인스턴스를 만들 때 호출 되는 특별한 메서드&lt;/b&gt;이다.&lt;br /&gt;이름대로, 인스턴스(객체)를 초기화 한다. &lt;code&gt;스페셜 메서드&lt;/code&gt; 혹은 &lt;code&gt;매직 메서드&lt;/code&gt; 라고 부른다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;self의 의미&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 생성할 때 뿐 아니라 클래스 안에서 속성을 사용할 때도 self.hello 처럼 self에 점을 붙여 사용하면 된다.&lt;br /&gt;self는 인스턴스 자기 자신을 의미한다.&lt;br /&gt;인스턴스 = 클래스(self) 에서 처럼 클래스를 호출하면 인스턴스가 자동으로 매개변수 self에 들어온다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;인스턴스 만들 때 값 받기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;init 에서 사용자에게 self에 값을 받으려면 매개변수를 통해 지정 가능하다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;class 클래스 이름:
    def __init__(self, 매개변수1, 매개변수2):
        self.속성1 = 매개변수1
        self.속성2 = 매개변수2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 이렇게 매개변수를 설정하고 나면, 객체를 생성할 때 반드시 매개변수에 값을 채워줘야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;print('{0} 저는 {1}입니다.'.format(self.hello, self.name))&lt;/code&gt; 에서 {0}, {1}에 format의 매개변수 값들이 올 수 있다.&lt;/p&gt;
&lt;h5&gt;비공개 속성 사용&lt;/h5&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스 안에서만 사용하고 외부에서는 사용할 수 없도록 하는 private 한 속성을 만들 때는 앞에 밑줄 두개를 붙여서 속성을 만들면 된다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;class 클래스이름:
     def __init__(self, 매개변수):
        self.__속성 = 값&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;클래스 속성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 다룬 내용은 클래스로 만들어 내는 인스턴스의 속성과 메서드였다.&lt;br /&gt;여기서 인스턴스가 아니라 클래스 자체에 속성을 만들 수도 있다.&lt;/p&gt;
&lt;pre class=&quot;flix&quot;&gt;&lt;code&gt;def 클래스 이름:
    속성 = 값&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 인스턴스에서 공통적으로 사용하는 공용 데이터를 만들 때 사용하는 방법이다.&lt;br /&gt;즉 &lt;b&gt;클래스 속성은 클래스에 속해 있으면서 모든 인스턴스에서 공유&lt;/b&gt;된다.&lt;br /&gt;인스턴스 전체가 사용해야 하는 값은 클래스 속성에, 각 인스턴스가 값을 따로 저장해야 할 때는 인스턴스 속성에 저장하면 된다.&lt;br /&gt;(타 언어에서 스태틱 변수와 같은 개념)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;self로도 클래스 속성에 접근은 가능하지만, 클래스 이름을 통해 접근하는 것이 더 명확한 코드가 된다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;class Person:
    species = 'human'  # 클래스 속성

    def __init__(self, name):
        self.name = name  # 인스턴스 속성

p1 = Person('홍길동')
p2 = Person('김철수')

print(p1.species, p2.species)  # human human
Person.species = 'homo sapiens'
print(p1.species, p2.species)  # homo sapiens homo sapiens
print(p1.name, p2.name)  # 홍길동 김철수&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값을 여러 사람이 공유하려면 클래스 속성, 공유하지 않으려면 인스턴스 속성을 사용하면 된다.&lt;br /&gt;앞서 다루었던 대로, 인스턴스 속성은 인스턴스별로 독립되어 있기 때문에 서로 영향을 주지 않는다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;속성을 찾을 때에는, 인스턴스에 속성이 있는지를 먼저 체크한 다음 클래스에 속성이 있는지 체크한다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h5&gt;&amp;nbsp;&lt;/h5&gt;
&lt;h5&gt;비공개 클래스 속성&lt;/h5&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마찬가지로 비공개로 클래스 속성을 만드려면 앞에 밑줄 두개를 붙여주면 된다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;정적 메서드&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메서드 위에 &lt;code&gt;@staticmethod&lt;/code&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;/li&gt;
&lt;li&gt;정적 메서드는 매개변수에 self를 지정하지 않는다(매개변수만 사용)&lt;/li&gt;
&lt;li&gt;인스턴스를 만들지 않고 메서드를 사용하고 싶을때 스태틱 메서드 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;클래스 메서드&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메서드 앞에 &lt;code&gt;@classmethod&lt;/code&gt;를 붙여 사용&lt;/li&gt;
&lt;li&gt;첫번째 매개 변수에 cls를 지정클래스 메소드는 정적 메소드와 유사한데, 내부적으로 자체적인 인스턴스를 추가할 수 있는 특성이 있다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@classmethod def class(cls): p = cls() return p&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;class 클래스 이름: @classmethod def 메서드(cls, 매개변수1, 매개변수2): 코드&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;클래스 상속&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;기능을 물려주는 클래스를 기반 클래스(base class)
&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;상속을 받아 새롭게 만드는 클래스를 파생 클래스(derived class)라고 한다.
&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;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;class 기반클래스이름: 코드

class 파생클래스이름(기반클래스이름):  
코드&lt;/code&gt;&lt;/pre&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;code&gt;__init__&lt;/code&gt;메서드를 호출하지 않으면 사용할 수 없기 때문에 &lt;code&gt;super()&lt;/code&gt;를 써서 호출해주어야 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파생 클래스에서 &lt;code&gt;__init__&lt;/code&gt; 을 아예 생략해버리면, 기반 클래스의 메서드를 자동으로 홏루하므로 &lt;code&gt;super()&lt;/code&gt;를 사용하지 않아도 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;메서드 오버라이딩&lt;/h4&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;code&gt;super().메서드()&lt;/code&gt; 로 실행하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;다중 상속&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;717&quot; data-origin-height=&quot;363&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mckGm/btsNaDW6sDk/cku2hq39X3shUzqwDVhe30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mckGm/btsNaDW6sDk/cku2hq39X3shUzqwDVhe30/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mckGm/btsNaDW6sDk/cku2hq39X3shUzqwDVhe30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmckGm%2FbtsNaDW6sDk%2Fcku2hq39X3shUzqwDVhe30%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;464&quot; height=&quot;235&quot; data-origin-width=&quot;717&quot; data-origin-height=&quot;363&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파생클래스의 매개변수로 기반 클래스 여러개를 넣으면, 여러 클래스로부터 상속을 받을 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;다이아몬드 상속&lt;/h5&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;936&quot; data-origin-height=&quot;582&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uq2WE/btsM91do2As/777f5qTc6llmjguRVvfFA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uq2WE/btsM91do2As/777f5qTc6llmjguRVvfFA0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uq2WE/btsM91do2As/777f5qTc6llmjguRVvfFA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fuq2WE%2FbtsM91do2As%2F777f5qTc6llmjguRVvfFA0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;474&quot; height=&quot;295&quot; data-origin-width=&quot;936&quot; data-origin-height=&quot;582&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;A를 상속받은 B,C를 다중상속사는 D가 있는 경우 다이아몬드 상속 이라고 한다.&lt;/li&gt;
&lt;li&gt;이런 명확하지 않고 애매한 상태는 좋지 않다.&lt;/li&gt;
&lt;li&gt;이 경우 중복되는 메서드가 모두 있으면 가장 가까운 상속의 메서드를 호출한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;메서드 탐색 순서 확인&lt;/h5&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다이아몬드 상속에 대한 해결책으로 파이썬에서는 메서드 탐색 순서(Method Resolution, MRO)를 따른다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;D.mro() # [&amp;lt;class '__main__.D'&amp;gt;, &amp;lt;class '__main__.B'&amp;gt;, ...]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;추상클래스&lt;/h4&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;code&gt;@abstractmethod&lt;/code&gt;를 통해 추상 클래스의 추상 메서드를 구현하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;from abc import *

class 추상클래스이름(metaclass=ABCMeta):  
  @abstractmethod  
  def 메서드이름(self): # 추상 메서드  
      코드&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;추상클래스를 상속받은 경우 추상 메서드를 모두 구현해야한다.&lt;/li&gt;
&lt;li&gt;오직 상속을 위한 용도로만 사용하고 싶을 때 추상 클래스를 사용한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;추상 클래스는 인스턴스로 만들 수 없다 -&amp;gt; 에러 발생&lt;/li&gt;
&lt;li&gt;파생클래스에서 반드시 구현해야할 메서드를 지정하는 용도로 사용된다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Language/Python</category>
      <author>_루디_</author>
      <guid isPermaLink="true">https://kimjoraeng.tistory.com/79</guid>
      <comments>https://kimjoraeng.tistory.com/79#entry79comment</comments>
      <pubDate>Fri, 4 Apr 2025 22:07:32 +0900</pubDate>
    </item>
    <item>
      <title>[Python] 딕셔너리(Dictionary)</title>
      <link>https://kimjoraeng.tistory.com/78</link>
      <description>&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;본 포스팅은 학교 수업에서 다룬 파이썬 문법 부분을 정리해두고자 남기는 포스팅이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;(해당 수업은 '파이썬 도장'과 '점프 투 파이썬'을 교재로 문법 수업을 진행)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1280&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nvQ3n/btsMPJKU203/BU14waSaY9CtE7DPLnRKG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nvQ3n/btsMPJKU203/BU14waSaY9CtE7DPLnRKG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nvQ3n/btsMPJKU203/BU14waSaY9CtE7DPLnRKG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnvQ3n%2FbtsMPJKU203%2FBU14waSaY9CtE7DPLnRKG1%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;304&quot; height=&quot;304&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1280&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;딕셔너리&lt;/h2&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;자바스크립트 객체 리터럴과 유사한 형태&amp;nbsp;&lt;/li&gt;
&lt;li class=&quot;ini&quot;&gt;&lt;code&gt;student = {'name': '홍길동', 'age': 20, 'grade': 'A'}&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;값마다 이름을 붙여 저장하는 방식이다.&lt;/li&gt;
&lt;li&gt;중괄호 안에 키 값 형식으로 저장&lt;/li&gt;
&lt;li&gt;각 키와 값은 &lt;code&gt;,(콤마)&lt;/code&gt;로 구분
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;딕셔너리 = {키1:값1, 키2:값2}&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;키를 먼저 지정하고 &lt;code&gt;:(콜론)&lt;/code&gt;을 붙여 값을 표현&lt;/li&gt;
&lt;li&gt;키는 값을 하나만 지정 가능한 키-값 쌍(key-value pair) 일대일 대응이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;키 이름이 중복되면?&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;student = {'name': '홍길동', 'age': 20, 'grade': 'A', 'age': 21}
print(student)  # {'name': '홍길동', 'age': 21, 'grade': 'A'}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;딕셔너리에 키와 값을 저장할 때 키가 중복되면 가장 뒤에 있는 값만 사용&lt;/li&gt;
&lt;li&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;/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;pre class=&quot;routeros&quot;&gt;&lt;code&gt;student = {1: '홍길동', 2.5: '이순신', True: '강감찬'}
print(student)  # {1: '강감찬', 2.5: '이순신'}&lt;/code&gt;&lt;/pre&gt;
빈 딕셔너리 만들기&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;딕셔너리 = &lt;code&gt;{}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;딕셔너리 = &lt;code&gt;dict()&lt;/code&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;d1 = {}
d2 = dict()
print(d1, d2)  # {} {}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;dict로 딕셔너리를 만들 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;딕셔너리 = dict(키1=값1, 키2=값2)&lt;/li&gt;
&lt;li&gt;인수로 리스트, 튜플, 딕셔너리를 넣을 수 있음
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;student = dict(name='홍길동', age=20)
print(student)  # {'name': '홍길동', 'age': 20}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;키=값 형식으로 만들 수도 있는데 이 때 키에 따옴표를 쓰면 안된다&lt;/li&gt;
&lt;li&gt;zip 함수를 쓸 수도 있다.&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;/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;pre class=&quot;routeros&quot;&gt;&lt;code&gt;student = {'name': '홍길동', 'age': 20}
student['grade'] = 'A'
print(student)  # {'name': '홍길동', 'age': 20, 'grade': 'A'}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;없는 키에서 값을 가져오려고 하면 에러 발생
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;print('name' in student)  # True
print('grade' in student)  # False&lt;/code&gt;&lt;/pre&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;딕셔너리에서 키가 있는지 확인하려면 in 연산자 사용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;있으면 True 없으면 False 반환
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;print('name' in student)  # True
print('grade' in student)  # False&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;len에 딕셔너리 변수를 넣어서 키의 개수를 구해도 되고, len에 딕셔너리를 그대로 넣어도 확인 가능&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Language/Python</category>
      <category>딕셔너리</category>
      <category>파이썬</category>
      <author>_루디_</author>
      <guid isPermaLink="true">https://kimjoraeng.tistory.com/78</guid>
      <comments>https://kimjoraeng.tistory.com/78#entry78comment</comments>
      <pubDate>Wed, 19 Mar 2025 23:58:47 +0900</pubDate>
    </item>
    <item>
      <title>[Python] 시퀀스 자료형(리스트, 튜플, range, 문자열)</title>
      <link>https://kimjoraeng.tistory.com/77</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;본 포스팅은 학교 수업에서 다룬 파이썬 문법 부분을 정리해두고자 남기는 포스팅이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;(해당 수업은 '파이썬 도장'과 '점프 투 파이썬'을 교재로 문법 수업을 진행)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1280&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJJuji/btsMQhAe0Gc/Xj01kxePCmVsCBPkihtL90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJJuji/btsMQhAe0Gc/Xj01kxePCmVsCBPkihtL90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJJuji/btsMQhAe0Gc/Xj01kxePCmVsCBPkihtL90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJJuji%2FbtsMQhAe0Gc%2FXj01kxePCmVsCBPkihtL90%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;323&quot; height=&quot;323&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1280&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;시퀀스&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;시퀀스 자료형&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리스트, 튜플, range, 문자열과 같이 연속적으로 값이 이어진 자료형을 시퀀스 자료형(sequence type)라고 한다.&lt;/li&gt;
&lt;li&gt;주로 사용하는 자료형은 &lt;code&gt;list&lt;/code&gt;, &lt;code&gt;tuple&lt;/code&gt;, &lt;code&gt;range&lt;/code&gt;, &lt;code&gt;str&lt;/code&gt;을 주로 사용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;bytes&lt;/code&gt;, &lt;code&gt;bytearray&lt;/code&gt; 도 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;시퀀스 자료형은 공통 동작과 기능을 제공한다&lt;/li&gt;
&lt;li&gt;시퀀스 객체 : 시퀀스 자료형으로 만든 객체&lt;/li&gt;
&lt;li&gt;요소 : 시퀀스 객체에 들어가는 각 값&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;특정값이 있는지 확인하기&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시퀀스 자료형(리스트, 튜플, range, 문자열)에서 &lt;code&gt;in&lt;/code&gt;과 &lt;code&gt;not in&lt;/code&gt; 연산자를 사용하여 특정 값의 존재 여부를 확인할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;# 1. 리스트에서 in, not in 사용
fruits = ['사과', '바나나', '오렌지', '포도']
print('사과' in fruits)      # True
print('키위' not in fruits)  # True

# 2. 튜플에서 in, not in 사용
numbers = (1, 2, 3, 4, 5)
print(3 in numbers)        # True
print(6 not in numbers)    # True

# 3. range에서 in, not in 사용
r = range(1, 10, 2)  # 1, 3, 5, 7, 9
print(5 in r)         # True
print(4 in r)         # False
print(2 not in r)     # True

# 4. 문자열에서 in, not in 사용
text = &quot;Hello, Python!&quot;
print('Python' in text)     # True
print('Java' not in text)   # True

# 5. 실제 활용 예시
def check_fruit(fruit_name):
    fruits = ['사과', '바나나', '오렌지', '포도']
    if fruit_name in fruits:
        return f&quot;{fruit_name}은(는) 과일 목록에 있습니다.&quot;
    else:
        return f&quot;{fruit_name}은(는) 과일 목록에 없습니다.&quot;

def is_odd_number(num):
    odd_numbers = range(1, 101, 2)  # 1부터 100까지의 홀수
    return num in odd_numbers

# 실행 결과
print(check_fruit('사과'))   # 사과은(는) 과일 목록에 있습니다.
print(check_fruit('키위'))   # 키위은(는) 과일 목록에 없습니다.
print(is_odd_number(27))    # True
print(is_odd_number(50))    # False

# 6. 여러 값 동시에 확인하기
numbers = (1, 2, 3, 4, 5)
print(all(x in numbers for x in [1, 2, 3]))  # True (모든 값이 있는지)
print(any(x in numbers for x in [1, 6, 7]))  # True (하나라도 있는지)&lt;/code&gt;&lt;/pre&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;code&gt;in&lt;/code&gt;: 시퀀스 내에 값이 있으면 &lt;code&gt;True&lt;/code&gt; 반환&lt;/li&gt;
&lt;li&gt;&lt;code&gt;not in&lt;/code&gt;: 시퀀스 내에 값이 없으면 &lt;code&gt;True&lt;/code&gt; 반환&lt;/li&gt;
&lt;li&gt;모든 시퀀스 자료형(list, tuple, range, str)에서 사용 가능&lt;/li&gt;
&lt;li&gt;대소문자를 구분함 (문자열의 경우)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;all()&lt;/code&gt;과 &lt;code&gt;any()&lt;/code&gt;를 활용하여 여러 값을 한 번에 확인 가능&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;시퀀스 객체 연결, 반복&lt;/h3&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;# 리스트 연결
list1 = [1, 2, 3]
list2 = [4, 5, 6]
combined_list = list1 + list2
print(combined_list)  # [1, 2, 3, 4, 5, 6]

# 리스트 반복
repeated_list = list1 * 3
print(repeated_list)  # [1, 2, 3, 1, 2, 3, 1, 2, 3]

# range는 + 연산자 불가능
r1 = range(1, 4)
r2 = range(4, 7)
# combined_range = r1 + r2  # TypeError 발생

# range를 리스트로 변환하여 연결
combined_range_list = list(r1) + list(r2)
print(combined_range_list)  # [1, 2, 3, 4, 5, 6]

# range를 튜플로 변환하여 연결
combined_range_tuple = tuple(r1) + tuple(r2)
print(combined_range_tuple)  # (1, 2, 3, 4, 5, 6)

# 문자열 연결
str1 = &quot;Hello, &quot;
str2 = &quot;World!&quot;
combined_str = str1 + str2
print(combined_str)  # &quot;Hello, World!&quot;&lt;/code&gt;&lt;/pre&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;code&gt;+&lt;/code&gt; 연산자로 연결 가능&lt;/li&gt;
&lt;li&gt;리스트는 &lt;code&gt;*&lt;/code&gt; 연산자로 반복 가능&lt;/li&gt;
&lt;li&gt;range는 직접 연결 불가, 리스트나 튜플로 변환 후 연결 가능&lt;/li&gt;
&lt;li&gt;문자열은 &lt;code&gt;+&lt;/code&gt; 연산자로 연결 가능하며, 반복은 &lt;code&gt;*&lt;/code&gt; 연산자로 가능&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;시퀀스 객체의 요소 개수 구하기&lt;/h3&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;# 리스트의 요소 개수 구하기
fruits = ['사과', '바나나', '오렌지', '포도']
print(len(fruits))  # 4

# 튜플의 요소 개수 구하기
numbers = (1, 2, 3, 4, 5)
print(len(numbers))  # 5

# 문자열의 길이 구하기
text = &quot;Hello, Python!&quot;
print(len(text))  # 14&lt;/code&gt;&lt;/pre&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;code&gt;len()&lt;/code&gt; 함수는 시퀀스 객체의 요소 개수를 반환합니다.&lt;/li&gt;
&lt;li&gt;리스트, 튜플, 문자열 등 모든 시퀀스 자료형에 사용 가능합니다.&lt;/li&gt;
&lt;li&gt;실무에서는 range등을 사용하여 리스트(튜플)을 생성하거나 다양한 방법으로 요소를 추가,삭제 하기 때문에 &lt;code&gt;len()&lt;/code&gt;를 자주 사용&lt;/li&gt;
&lt;/ol&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;/li&gt;
&lt;li&gt;시퀀스 객체에 &lt;code&gt;[](대괄호)&lt;/code&gt;를 붙이고 안에 각 요소의 인덱스를 지정하면 해당 요소에 접근 가능
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;fruits = ['사과', '바나나', '오렌지']
print(fruits[0])  # 사과
print(fruits[-1])  # 오렌지&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;시퀀스 객체의 인덱스는 항상 0부터 시작&lt;/li&gt;
&lt;li&gt;튜플, 문자열, range도 인덱스로 접근 가능&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;즉 -1은 뒤에서 첫 번째, -5는 뒤에서 다섯 번째 요소
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;fruits = ['사과', '바나나', '오렌지']
print(fruits[-1])  # 오렌지
print(fruits[-2])  # 바나나&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;마찬가지로 튜플,문자열,range모두 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;인덱스 범위를 벗어나면?&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인덱스 범위를 벗어나면 IndexError발생&lt;/li&gt;
&lt;li&gt;마지막 요소에 접근하려면 -1로 접근&lt;/li&gt;
&lt;li&gt;len 함수로 리스트의 길이를 구한 뒤 길이-1를 인덱스로 지정
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;fruits = ['사과', '바나나', '오렌지']
# print(fruits[3])  # IndexError 발생&lt;/code&gt;&lt;/pre&gt;
요소에 값 할당&lt;/li&gt;
&lt;li&gt;[]로 시퀀스 객체의 요소에 접근한 뒤 =로 값을 할당&lt;/li&gt;
&lt;li&gt;변경은 리스트만 가능하고, 튜플, 문자열, range는 변경이 불가능하다.
&lt;pre class=&quot;prolog&quot;&gt;&lt;code&gt;fruits = ['사과', '바나나', '오렌지']
fruits[1] = '키위'
print(fruits)  # ['사과', '키위', '오렌지']&lt;/code&gt;&lt;/pre&gt;
요소 삭제&lt;/li&gt;
&lt;li&gt;&lt;code&gt;del&lt;/code&gt;을 톡해 삭제할 요소 지정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;del 시퀀스객체[인덱스]&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이경우에도 리스트만 삭제가 가능하고 다른 자료형은 불가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;슬라이스&lt;/h3&gt;
&lt;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;/ul&gt;
&lt;/li&gt;
&lt;li&gt;⭐️ 원본은 바뀌지 않고, 필요한 부분을 잘라내어 새로운 리스트를 만들 수 있다.
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;a[0:10] #인덱스 0부터 9까지 잘라서 새 리스트 만듦&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;슬라이스 했을 때 실제로 가져오는 요소는 인덱스부터 끝인덱스 -1까지임
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;a[1:1]&lt;/code&gt;처럼 시작 인덱스와 끝 인덱스를 같은 숫자로 지정하면 아무것도 가져오지 않음&lt;/li&gt;
&lt;li&gt;&lt;code&gt;a[1:2]&lt;/code&gt;처럼 1을 더 크게 지정해야 요소 하나를 가져온다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;리스트 중간 부분 가져오기
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;a=[0,10,20,30,40,50,60,70,80,90]
&amp;gt;&amp;gt;&amp;gt; a[4:7] #인덱스 4부터 6까지 요소 3개 가져옴
[40,50,60]&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;이 때 음수 인덱스를 사용해서 중간부터 끝까지를 지정할 수도 있음
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;인덱스 &lt;b&gt;증가폭을 지정&lt;/b&gt;하여 범위 내에서 인덱스를 건너뛰어가며 요소를 가져올 수도 있다
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;a=[0,10,20,30,40,50,60,70,80,90]
&amp;gt;&amp;gt;&amp;gt; a[2:8:3] # 인덱스 2부터 3씩 증가시키면서 인덱스 7까지 가져옴
[20, 50]&lt;/code&gt;&lt;/pre&gt;
(사진 자료 추가)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;인덱스 생략도 가능&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;처음을 생략하여 리스트의 처음부터 7인덱스 -1까지로
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;a[:7]&lt;/code&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;code&gt;a[7:]&lt;/code&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;code&gt;a[:]&lt;/code&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;처음 인덱스를 생략하여 인덱스 증가폭을 지정하고 리스트의 처음부터 인덱스를 증가시키면서 끝 인덱스 -1까지 요소를 가져오기
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;a[:7:2]&lt;/code&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;code&gt;a[7::2]&lt;/code&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;code&gt;a[::2]&lt;/code&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;code&gt;a[::]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;len을 응용하여 전체를 가져오기
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;a[0:len(a)]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;마찬가지로 생략과 함께 활용 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;슬라이스는 튜플, range, 문자열에도 적용 가능
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;range에 슬라이스 사용하면 range객체를 새로 만듦(함수형태)
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;r = range(10)
&amp;gt;&amp;gt;&amp;gt; r
range(0,10)
&amp;gt;&amp;gt;&amp;gt; r[4:7]
range(4,7)
&amp;gt;&amp;gt;&amp;gt; r[4:]
range(4, 10)&lt;/code&gt;&lt;/pre&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;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;a = [0, 1, 2, 3, 4, 5]
a[1:4] = [10, 20, 30]
print(a)  # [0, 10, 20, 30, 4, 5]&lt;/code&gt;&lt;/pre&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;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;# 할당할 요소 개수가 적은 경우
a = [0, 1, 2, 3, 4, 5]
a[1:4] = [10] # 값 1,2,3이 사라지고 그 자리에 10이 들어감
print(a)  # [0, 10, 4, 5]
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;할당할 요소 개수가 더 많은 경우&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;a = [0, 1, 2, 3, 4, 5]&lt;br /&gt;a[1:4] = [10, 20, 30, 40] # 1,2,3이 사라지고 10~40 값이 들어감&lt;br /&gt;print(a) # [0, 10, 20, 30, 40, 4, 5]&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;- 인덱스 증가폭 지정과 함께 활용하여, 건너뛰면서 값 할당도 가능
```python
a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
a[2:8:2] = ['a', 'b', 'c'] # 인덱스 2부터 2씩 증가시키면서 인덱스 7까지 값 할당
&amp;gt;&amp;gt;&amp;gt; a #[0, 1, 'a', 3, 'b', 5, 'c', 70, 80, 90]&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;증가폭을 지정했을 때는 &lt;b&gt;슬라이스 범위의 요소 개수와 할당할 요소 개수가 정확히 일치해야한다.&lt;/b&gt; 일치하지 않을 경우 Value Error 발생&lt;/li&gt;
&lt;li&gt;리스트 외에 튜플, range, 문자열은 &lt;b&gt;슬라이스로 범위지정 해도 요소 할당은 못함&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;del로 슬라이스 삭제&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;슬라이스 삭제는 del뒤에 삭제할 범위를 지정 가능
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;del 시퀀스객체[시작인덱스:끝인덱스]
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
del a[2:5]
print(a)  # [0, 1, 5, 6, 7, 8, 9]&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;del로 요소 삭제시 원래 있던 리스트가 변경되며 새 리스트는 생성되지 않는다&lt;/li&gt;
&lt;li&gt;증가폭 지정해서 del 가능
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
del a[2:8:2] #인덱스 2부터 2씩 증가시키면서 인덱스 6까지 삭제
print(a)  # [0, 1, 3, 5, 7, 8, 9]&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;튜플, range, 문자열은 del로 삭제 불가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 풀이에 용이한 문법&lt;/h2&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;이론 강의에서 다룬 문법은 아니지만 실습 시간에 파이썬을 원래 학습했던 학우들이 문제풀이 발표할 때 사용하는 문법들을 보고 유용하다고 판단하여 따로 찾아보았다.&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;리스트 컴프리헨션&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬에서 리스트는 리스트 안에 for문과 if문을 사용할 수 있다는 특이점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 리스트 컴프리헨션이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'식으로 지정해서 생성된 것을 리스트로 잡아둔다' 는 뜻으로 이해하면 될 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트를 만드는 방법은 다양한데, 리스트 컴프리헨션을 사용하면 여러 줄로 생성할 리스트 코드를 한줄로 줄일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문법은 [식 for 변수 in 리스트] 혹은 list(식 for 변수 in 리스트) 의 형태로 사용된다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1757687622440&quot; class=&quot;lsl&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; a = [i for i in range(10)]        # 0부터 9까지 숫자를 생성하여 리스트 생성
&amp;gt;&amp;gt;&amp;gt; a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
&amp;gt;&amp;gt;&amp;gt; b = list(i for i in range(10))    # 0부터 9까지 숫자를 생성하여 리스트 생성
&amp;gt;&amp;gt;&amp;gt; b
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RnIob/btsM2mOmLAp/QPvdQLcBlPTNb2J0uyg3s0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RnIob/btsM2mOmLAp/QPvdQLcBlPTNb2J0uyg3s0/img.png&quot; data-alt=&quot;리스트 표현식의 동작 순서&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RnIob/btsM2mOmLAp/QPvdQLcBlPTNb2J0uyg3s0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRnIob%2FbtsM2mOmLAp%2FQPvdQLcBlPTNb2J0uyg3s0%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;456&quot; height=&quot;342&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;리스트 표현식의 동작 순서&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;for문과 함께&amp;nbsp; if문을 사용하거나, 한번에&amp;nbsp; 두개 이상의 for문을 사용할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[식 for&amp;nbsp; 변수1 in 리스트1 if 조건식1&amp;nbsp; for 변수2 in 리스트2 if 조건식 2 ... ]&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;unpacking&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트의 모든 요소를 꺼내서 출력해야 할 때 항상 for문을 사용했었는데,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬의 unpacking 문법을 쓰면 한줄로 처리가 가능하다고 한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1757687622441&quot; class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;a = [1,2,3] # 123 을 이어서 출력하고 싶을 때
for i in range(0,len(a)):
	print(i, sep=&quot;&quot;, end=&quot;&quot;) # 123
    

print(*a, sep=&quot;&quot;) # 123, 같은 출력&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;unpacking&lt;/b&gt;은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;*&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;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;f-string&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열에서&amp;nbsp; 특정 부분만 바꾸고 나머지 부분은 일정하다고 할 때, 문자열 포맷팅을 사용하면&amp;nbsp; 손쉽게 출력이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용하는 방법은 javascript의 템플릿 문법과 유사하다.&lt;/p&gt;
&lt;pre id=&quot;code_1757687622442&quot; class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;print(&quot;2025년 1월&quot;)
print(&quot;2025년 2월&quot;)
print(&quot;2025년 3월&quot;)
...
print(&quot;2025년 12월&quot;)




month  = 1
while  month &amp;lt;= 12;
	print(f&quot;2025년 {month}월&quot;)
    month  = month  + 1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 print를 여러줄 사용한 코드와 아래&amp;nbsp; while문의 출력 내용은 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 3.6버전부터 사용이 가능한 기능이며,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;f와 {}&lt;/b&gt;만 사용하면 되어 간단하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열의 맨 앞에 f를 붙여주고, 중괄호 안에 직접 변수나 출력하고 싶은 것을 넣어준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때, 중괄호에 콜론(:)을&amp;nbsp; 사용하면 정렬하는 기능도 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1757687622442&quot; class=&quot;makefile&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;s1='left'
result1 = f'|{s1:&amp;lt;10}|'

s2='center'
result2 = f'|{s1:^10}|'

s3='right'
result3 = f'|{s1:&amp;gt;10}|'

print(result1) # |left    |
print(result2) # | center |
print(result3) # |   right|&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중괄호 안에서도 중괄호를 출력하고 싶으면 중괄호 두개를 사용하면 되고, 중괄호와 함께 변수의 값이 아니라 변수 이름을 그대로 출력하고 싶으면 3개를 쓰면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리와 함께 쓸 때에는 중괄호 안에서 d와 대괄호를 통해 원하는 값을 가져올 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1757687622443&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;d&amp;nbsp;=&amp;nbsp;{'name':&amp;nbsp;'BlockDMask',&amp;nbsp;'gender':&amp;nbsp;'man',&amp;nbsp;'age':&amp;nbsp;100}
result&amp;nbsp;=&amp;nbsp;f'my&amp;nbsp;name&amp;nbsp;{d[&quot;name&quot;]},&amp;nbsp;gender&amp;nbsp;{d[&quot;gender&quot;]},&amp;nbsp;age&amp;nbsp;{d[&quot;age&quot;]}'
print(result)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고&lt;/h2&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;</description>
      <category>Language/Python</category>
      <category>리스트</category>
      <category>시퀀스</category>
      <category>튜플</category>
      <category>파이썬</category>
      <author>_루디_</author>
      <guid isPermaLink="true">https://kimjoraeng.tistory.com/77</guid>
      <comments>https://kimjoraeng.tistory.com/77#entry77comment</comments>
      <pubDate>Wed, 19 Mar 2025 23:56:57 +0900</pubDate>
    </item>
    <item>
      <title>[Network] HTTP 프로토콜의 이해</title>
      <link>https://kimjoraeng.tistory.com/75</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;지난 포스팅에서 네트워크의 애플리케이션 계층에 대해 다뤘는데 이번에는 그 중에서도 대표적인 HTTP를 다뤄보려고 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;288&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Al2qu/btsMK2JolxS/mKRjd7UBjq4s70OBMgrJ31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Al2qu/btsMK2JolxS/mKRjd7UBjq4s70OBMgrJ31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Al2qu/btsMK2JolxS/mKRjd7UBjq4s70OBMgrJ31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAl2qu%2FbtsMK2JolxS%2FmKRjd7UBjq4s70OBMgrJ31%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;512&quot; height=&quot;288&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;288&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 웹과 HTTP의 기본 개념&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;웹페이지는 아래와 같은 다양한 객체들로 구성되어 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HTML 파일&lt;/li&gt;
&lt;li&gt;JPEG 이미지 파일&lt;/li&gt;
&lt;li&gt;Java 애플릿&lt;/li&gt;
&lt;li&gt;오디오 파일 등&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 웹페이지는 기본 HTML 파일(보통 index.html)을 중심으로 여러 참조 객체들이 연결되어 있는 구조다. 각 객체는 고유한 URL을 통해 접근할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.2 클라이언트-서버 통신&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트: 필요한 객체에 대한 요청(request)을 보냄&lt;/li&gt;
&lt;li&gt;서버: 요청받은 객체를 응답(response)으로 전송&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.3 HTTP의 특징&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;TCP&lt;/code&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;사용 후에는 반드시 연결 해제(disconnect) 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Stateless(무상태) 프로토콜
&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. HTTP 연결 방식&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP 연결 방식은 크게 3가지가 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.1 Non-persistent HTTP&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;한 번의 TCP 연결로 하나의 객체만 전송 가능&lt;/li&gt;
&lt;li&gt;여러 객체 다운로드시 매번 새로운 TCP 연결 필요&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;매 요청마다 TCP 연결/해제 오버헤드 발생&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;2.2 Persistent HTTP&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나의 TCP 연결로 여러 객체 전송 가능&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;TCP 연결 설정/해제 오버헤드 감소&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;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.3 파이프라이닝(Pipelining)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Persistent HTTP의 성능 향상을 위한 기술&lt;/li&gt;
&lt;li&gt;응답을 기다리지 않고 여러 요청을 연속해서 전송&lt;/li&gt;
&lt;li&gt;서버는 요청 순서대로 응답 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Non-persistent HTTP 통신 과정&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;HTTP 클라이언트가 서버로 TCP 연결 시도 (기본 포트 80 사용)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;3-way handshake&lt;/code&gt;를 통한 연결 수립&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;서버의 TCP 연결 수락 및 확인&lt;/li&gt;
&lt;li&gt;클라이언트가 HTTP request 메시지 전송
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;GET&lt;/code&gt;/&lt;code&gt;POST&lt;/code&gt; 등의 메소드와 URL 정보 포함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;서버가 요청받은 객체의 URL이 포함된 base HTML을 response로 전송
&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;서버의 TCP 연결 해제 (&lt;code&gt;4-way handshake&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;클라이언트가 HTML 파일을 받아 브라우저에 표시&lt;/li&gt;
&lt;li&gt;HTML 파일 내의 각 참조 객체에 대해 1-6 과정 반복&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1 응답 시간 분석&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 통신을 하는 과정에서 걸리는 시간을 분석하여 네트워크 성능을 평가하고 최적화할 수 있다. 특히 Non-persistent HTTP에서는 매 객체마다 새로운 TCP 연결이 필요하므로, 전체 페이지 로딩 시간이 크게 증가할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 사용자 경험에 직접적인 영향을 미치므로 응답 시간 분석과 개선이 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트-서버 간 패킷 왕복 시간을 RTT(Round Trip Time)라는 단위로 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 총 소요 시간 = 2RTT + 파일 전송 시간 으로 볼 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TCP 연결: 1RTT
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;3-way handshake를 통한 연결 수립에 필요한 시간&lt;/li&gt;
&lt;li&gt;SYN, SYN+ACK, ACK 패킷 교환&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;HTML 파일 요청/수신: 1RTT
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HTTP 요청 메시지 전송과 응답 수신에 걸리는 시간&lt;/li&gt;
&lt;li&gt;GET/POST 요청과 서버 응답 포함&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;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;※ 참조된 객체마다 위 과정이 반복되므로 총 지연 시간은:&lt;br /&gt;총 지연 = N &amp;times; (2RTT + 파일 전송 시간), N = 객체 수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 연결을 하는데에 쓰이는 시간이 고정적으로 소요되므로 연결을 매번 새롭게 하는 것은 시간의 낭비가 심하다고 볼 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. Persistent HTTP의 특징과 장점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Persistent HTTP는 Non-persistent HTTP의 단점을 보완하기 위해 등장한 방식이다.&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;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TCP 연결을 새로 설정하는 시간을 절약&lt;/li&gt;
&lt;li&gt;동일한 서버와 통신할 때는 기존 연결을 재사용&lt;/li&gt;
&lt;li&gt;웹 페이지의 이미지나 스크립트 같은 참조 객체를 발견하면 즉시 요청할 수 있다&lt;/li&gt;
&lt;li&gt;대부분의 경우 &lt;b&gt;하나의 RTT(Round Trip Time)로 처리가 가능&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;서버가 응답 후에도 연결 유지&lt;/li&gt;
&lt;li&gt;TCP 연결 설정 시간 절약&lt;/li&gt;
&lt;li&gt;동일 서버와의 통신은 기존 연결 재사용&lt;/li&gt;
&lt;li&gt;참조 객체 발견 즉시 요청 가능&lt;/li&gt;
&lt;li&gt;일반적으로 하나의 RTT로 처리 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. HTTP 메시지 구조와 통신 방식&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP 메시지는 클라이언트와 서버가 &lt;b&gt;서로 대화하는 방식을 정의&lt;/b&gt;한다. 마치 우리가 편지를 주고받듯이, 정해진 형식에 따라 정보를 교환하는 것이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.1 Request 메시지의 구조&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Request 메시지는 클라이언트가 서버에게 보내는 '요청서'라고 생각하면 된다. ASCII 형식으로 작성되며, 크게 세 부분으로 구성된다:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Request line (요청 행)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;GET&lt;/code&gt;: 정보를 요청할 때 사용한다&lt;/li&gt;
&lt;li&gt;&lt;code&gt;POST&lt;/code&gt;: 새로운 정보를 전송할 때 사용한다&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HEAD&lt;/code&gt;: 헤더 정보만 요청할 때 사용한다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Header lines (헤더 정보)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Host&lt;/code&gt;: 요청하는 웹사이트 주소&lt;/li&gt;
&lt;li&gt;&lt;code&gt;User-Agent&lt;/code&gt;: Chrome, Firefox 등 브라우저 정보&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Connection&lt;/code&gt;: keep-alive로 설정하면 연결을 유지한다&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Accept-Language&lt;/code&gt;: 선호하는 언어 설정&lt;/li&gt;
&lt;li&gt;&lt;code&gt;If-Modified-Since&lt;/code&gt;: 캐시된 데이터의 최종 수정일&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Date&lt;/code&gt;: 요청 생성 시간&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Body (선택적)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;POST&lt;/code&gt; 요청의 경우 전송할 데이터를 포함한다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;※ HTTP/1.1부터는 PUT(수정), DELETE(삭제) 등의 메소드가 추가되어 더 다양한 작업을 수행할 수 있게 되었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.2 Response 메시지의 구조&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버는 클라이언트의 요청에 대해 Response 메시지로 답변을 보낸다:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Status line (상태 행)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;200: 요청이 성공적으로 처리됨&lt;/li&gt;
&lt;li&gt;404: 요청한 페이지를 찾을 수 없음&lt;/li&gt;
&lt;li&gt;500: 서버에서 내부 오류가 발생함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Header lines (헤더 정보)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Keep-Alive: 연결 유지 시간&lt;/li&gt;
&lt;li&gt;Content-Type: 응답 데이터의 형식&lt;/li&gt;
&lt;li&gt;Server: 서버 소프트웨어 정보&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Body (응답 본문)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HTML, 이미지 등 실제 요청된 데이터가 포함된다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;마무리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 HTTP는 다뤄야 하는 내용이 너무 방대하다. 이 포스팅은 학교 전공 수업의 내용 일부를 정리한 글에 불과하지만 수강 이후에도 기억해야한다고 생각하는 내용을 최대한 간결하게 정리해보았다.&lt;/p&gt;</description>
      <category>CS 지식/네트워크</category>
      <author>_루디_</author>
      <guid isPermaLink="true">https://kimjoraeng.tistory.com/75</guid>
      <comments>https://kimjoraeng.tistory.com/75#entry75comment</comments>
      <pubDate>Fri, 14 Mar 2025 03:03:09 +0900</pubDate>
    </item>
  </channel>
</rss>