<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>HaeSung's Development Blog</title>
    <link>https://juniortech.tistory.com/</link>
    <description>개발 공부 개발 공부</description>
    <language>ko</language>
    <pubDate>Mon, 15 Jun 2026 14:46:04 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>HaeSungDev</managingEditor>
    <image>
      <title>HaeSung's Development Blog</title>
      <url>https://tistory1.daumcdn.net/tistory/2820998/attach/31f4370762f44537b63a2698e001ae40</url>
      <link>https://juniortech.tistory.com</link>
    </image>
    <item>
      <title>성공과 실패를 결정하는 1%의 네트워크 원리 6장</title>
      <link>https://juniortech.tistory.com/26</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번 장에서는 웹 서버에 HTTP 요청이 수신되고 처리 후 브라우저에 응답을 주는 과정을 정리하겠습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 서버의 개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버가 요청을 받기 위해서는 클라이언트가 요청을 송신할 때와 동일하게 소켓과 프로토콜 스택을 이용합니다. 하지만 클라이언트와 사용하는 방법이 다릅니다. 클라이언트가 tcp 연결을 할 때는 서버에 바로 연결을 진행하면 되지만 서버는 클라이언트와 송수신을 하기 위해 접속을 대기하는 과정이 필요합니다. 따라서 서버는 접속을 대기하는 부분과 접속 이후 클라이언트와 송수신 하는 부분으로 프로그램이 나뉩니다.&lt;/p&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;lt;서버IP&amp;gt;,&amp;lt;서버PORT&amp;gt;&amp;lt;클라이언트IP&amp;gt;&amp;lt;클라이언트PORT&amp;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;해당 정보가 있으면 디스크립터가 필요없다고 생각할 수 있는데 접속 대기 이후 소켓에 해당 정보를 설정하기 전에는 소켓을 식별할 방법이 디스크립터 밖에 없으므로 디스크립터가 필요합니다. 또한 디스크립터의 값이 사용하기 훨씬 간단한 이유도 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 서버의 수신 동작&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버의 수신은 LAN 어댑터에서 패킷의 전기 신호를 데이터로 변환하는 작업부터 시작됩니다. LAN 어댑터가 전기 신호를 읽어서 디지털 데이터로 변환이 되면 LAN 어댑터의 버퍼에 저장이 되고, 인터럽트를 통해 CPU에 통지합니다. 통지를 받은 CPU는 LAN 드라이버를 실행해서 해당 버퍼를 읽어서 프로토콜 스택에 전달합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로토콜 스택은 읽은 패킷의 MAC 어드레스가 서버의 MAC과 동일한지 확인하고 패킷을 검증한 후 해당하는 소켓을 찾아서 데이터를 전달합니다. 만약 SYN 패킷이라면 연결 과정을 진행합니다. IP 패킷이 분할 되었으면 합치는 과정도 진행합니다.&lt;/p&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;h2 data-ke-size=&quot;size26&quot;&gt;3. 웹서버 소프트웨어가 리퀘스트 메시지의 의미를 해석하여 요구에 응한다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 서버가 리퀘스트를 수신한 이후에는 이를 분석해서 처리하는 과정을 진행합니다. 요청 헤더와 요청 바디를 이용해서 처리를 합니다. 요청 메소드와 URI를 이용해서 처리를 할 때 두가지 방식이 있는데(옛날 방식으로, 현재는 다른 방식을 많이 씀) 파일에서 데이터를 읽어서 응답해주는 방식과, cgi 프로그램을 실행 시켜 결과를 반환하는 방식이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일에서 데이터를 읽을 때는 보안 문제가 있으므로 URI에 가상 디렉토리를 사용해서 실제 디렉토리와 매핑하는 과정이 있습니다. CGI를 호출할 때는 OS에 프로세스 실행을 요청하면서 요청 데이터를 전달합니다.&lt;/p&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;h2 data-ke-size=&quot;size26&quot;&gt;4. 웹 브라우저가 응답 메시지를 받아 화면에 표시한다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저가 웹 서버로 부터 응답 메세지를 받으면 이를 분석해서 화면에 표시합니다. 이때 Content-Type 헤더를 이용해서 어떤 데이터 타입인지 분석하고, 타입에 맞도록 데이터를 화면에 표시합니다.&lt;/p&gt;</description>
      <category>Study/성공과 실패를 결정하는 1%의 네트워크 원리</category>
      <author>HaeSungDev</author>
      <guid isPermaLink="true">https://juniortech.tistory.com/26</guid>
      <comments>https://juniortech.tistory.com/26#entry26comment</comments>
      <pubDate>Sun, 13 Feb 2022 18:10:41 +0900</pubDate>
    </item>
    <item>
      <title>02 TCP/IP의 데이터를 전기 신호로 만들어 보낸다</title>
      <link>https://juniortech.tistory.com/25</link>
      <description>&lt;p&gt;이번 장에서는 브라우저가 HTTP 요청을 하기 위해 소켓을 작성하고 서버에 접속하는 과정과 TCP/IP, 이더넷을 통해 데이터를 송수신 하는 과정에 대해 알아봅니다.&lt;/p&gt;
&lt;h2&gt;1. 소켓을 작성한다&lt;/h2&gt;
&lt;p&gt;브라우저에서 서버에 접속하려면 OS의 프로토콜 스택에 요청해야 하는데 이 때 소켓을 사용합니다. 소켓은 소켓 라이브러리를 이용해서 생성할 수 있습니다.&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;소켓은 내부적으로 프로세스 통신을 제어하기 위한 제어 정보들을 메모리에 저장하고, 이 정보들을 이용해서 프로토콜 스택에 송수신을 요청합니다.&lt;/p&gt;
&lt;h2&gt;2. 서버에 접속한다&lt;/h2&gt;
&lt;p&gt;HTTP 요청을 위해 소켓을 생성했으면 그 다음은 서버에 접속을 해야합니다. 서버에 접속하려면 서버의 IP와 서버에 있는 애플리케이션의 포트를 알아야 합니다. 이는 서버 URL에서 구할 수 있습니다. DNS를 통해 ip를 얻고, URL 구조에서 포트를 얻으면 됩니다.&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;ip와 port를 구했으므로 소켓의 connect 함수를 이용해서 서버에 접속을 요청할 수 있습니다. HTTP2 버전까지는 TCP/IP 프로토콜을 사용하는데 TCP는 요청 데이터를 전달하기 전 3-WAY Handshake라는 연결 파이프를 생성하는 과정을 진행합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1716&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLJLqC/btrrJthjWRV/cNF555kcKBJM2BJ7HkrhJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLJLqC/btrrJthjWRV/cNF555kcKBJM2BJ7HkrhJk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLJLqC/btrrJthjWRV/cNF555kcKBJM2BJ7HkrhJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLJLqC%2FbtrrJthjWRV%2FcNF555kcKBJM2BJ7HkrhJk%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;1920&quot; height=&quot;1716&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1716&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;위 사진을 참고하면 연결 파이프를 생성하는 과정을 볼 수 있습니다. 클라이언트가 먼저 서버에게 SYN 패킷을 보내고 서버가 SYN + ACK 패킷을 보낸 다음 클라이언트가 ACK 패킷을 보내면 연결이 맺어집니다. 여기서는 자세한 설명은 생략했습니다.&lt;/p&gt;
&lt;h2&gt;3. 데이터를 송수신한다&lt;/h2&gt;
&lt;p&gt;서버와 연결을 맺었다면 브라우저는 요청 데이터를 전송합니다. 요청 데이터는 소켓을 통해 프로토콜 스택으로 전달되고, 프로토콜 스택은 이를 패킷으로 만듭니다. 패킷에는 TCP/IP 프로토콜을 이용하기 위해 TCP 헤더와 IP 헤더가 추가됩니다.&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;패킷의 크기는 이더넷에서 1,500바이트 MTU에서 IP 헤더 20바이트(기본 옵션), TCP 헤더 20바이트(기본 옵션)를 제외한 MSS라는 값으로 제한된 1,460바이트 입니다. 따라서 이 크기를 넘는 요청은 패킷이 분할됩니다.&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;패킷이 모두 만들어졌으면 LAN 어댑터로 전송을 요청하는데, 이 때 이더넷 프로토콜을 사용하고 MAC 헤더가 필요하므로 프로토콜 스택에서 MAC 헤더를 추가해줍니다. 이는 뒤에서 더 자세히 나옵니다.&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;패킷이 발송되면 중간에 허브와 라우터를 통해서 전달되고 서버에서는 이를 받아서 패킷을 분석하고 데이터를 수신합니다. 그 후 ACK 패킷을 날려 데이터를 받았다고 통지합니다. 이 때 패킷을 하나씩 전달하면 비효율적이므로 슬라이딩 윈도우를 이용하는데 자세한 설명은 너무 길어지므로 생략하겠습니다.&lt;/p&gt;
&lt;p&gt;마지막으로 서버는 수신받은 요청을 처리해서 응답을 동일한 통신 구조로 클라이언트에게 전달합니다.&lt;/p&gt;
&lt;h2&gt;4. 서버에서 연결을 끊어 소켓을 말소한다&lt;/h2&gt;
&lt;p&gt;이제 요청과 응답이 완료됐으므로 연결을 종료합니다. HTTP 1.1에서는 기본적으로 keep-alive를 사용하지만 여기서는 바로 끊는다고 가정합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhcCVr/btrrDohgbJv/L0dqmRzRSq9az5hoirc6N0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhcCVr/btrrDohgbJv/L0dqmRzRSq9az5hoirc6N0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhcCVr/btrrDohgbJv/L0dqmRzRSq9az5hoirc6N0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhcCVr%2FbtrrDohgbJv%2FL0dqmRzRSq9az5hoirc6N0%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;1920&quot; height=&quot;1536&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1536&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;연결을 끊을 때는 위와 같이 4-WAY Handshake로 진행합니다. 자세한 설명은 생략하겠습니다.&lt;/p&gt;
&lt;h2&gt;5. IP와 이더넷의 패킷 송수신동작&lt;/h2&gt;
&lt;p&gt;프로토콜 스택에서 만들어진 패킷은 LAN 어댑터으로 전달되고, LAN 어댑터는 이더넷으로 패킷을 전송합니다. LAN 어댑터에 패킷을 전달하기 전에 이더넷 프로토콜을 위해 MAC 헤더를 추가 해야 합니다. 목적지 MAC 어드레스는 ARP 프로토콜을 사용해서 알아냅니다. IP 주소를 네트워크에 브로드캐스팅하면 목적지 IP를 가지고 있는 노드 또는 목적지 IP에 전달할 수 있는 라우터가 MAC 주소를 응답합니다.&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;MAC 어드레스 목적지를 알아냈으니 이를 MAC 헤더에 추가하고 LAN 어댑터에 전달하면 이를 전기 신호로 변환해서 목적지에 전송합니다. 자세한 설명은 생략하겠습니다.&lt;/p&gt;
&lt;h2&gt;6. UDP 프로토콜을 이용한 송수신 동작&lt;/h2&gt;
&lt;p&gt;TCP 프로토콜은 작은 데이터를 전송하기에는 DNS 같은 1회성 요청에 적합하지 않습니다. 또는 비디오나 음성 같이 실시간성이 중요한 데이터를 전송할 때도 적합하지 않습니다. 따라서 연결이 필요없고, 데이터 전송 확인이 필요없는 UDP를 사용합니다.&lt;/p&gt;</description>
      <category>Study/성공과 실패를 결정하는 1%의 네트워크 원리</category>
      <author>HaeSungDev</author>
      <guid isPermaLink="true">https://juniortech.tistory.com/25</guid>
      <comments>https://juniortech.tistory.com/25#entry25comment</comments>
      <pubDate>Tue, 25 Jan 2022 20:33:30 +0900</pubDate>
    </item>
    <item>
      <title>실용주의 프로그래머 정리 8</title>
      <link>https://juniortech.tistory.com/24</link>
      <description>&lt;h2&gt;실용주의 프로젝트&lt;/h2&gt;
&lt;p&gt;이번 장에서는 실용주의 기법들을 팀과 프로젝트에 적용하는 방법에 대해 알아보겠습니다.&lt;/p&gt;
&lt;h3&gt;41. 실용주의 팀&lt;/h3&gt;
&lt;p&gt;이전 장들에서 배운 실용주의 기법들을 팀 전체에 적용하는 전략을 알아보겠습니다.&lt;/p&gt;
&lt;h4&gt;깨진 창문을 없애라&lt;/h4&gt;
&lt;p&gt;깨진 창문은 팀원 전체가 관심을 가지고 정리해야합니다. 코드를 정리하는 인원 몇명을 두는 것은 현실적으로 불가능합니다. 팀 전체가 지저분하거나 개선해야되는 코드를 방치하지 말고 깨끗히 정리합시다.&lt;/p&gt;
&lt;h4&gt;삶은 개구리&lt;/h4&gt;
&lt;p&gt;프로젝트 전체 환경 변화를 지속적으로 감시하고 관찰해야 합니다. 아무도 모르게 프로젝트가 망가질 수 있습니다. 필요하다면 감시자 역할을 팀원 중 한명에게 맡겨서 변화를 지속적으로 감시하도록 할 수 있습니다.&lt;/p&gt;
&lt;h4&gt;소통하라!&lt;/h4&gt;
&lt;p&gt;팀원들끼리 활발하게 소통해야합니다. 또한 외부와 적극적으로 소통해야합니다. 무뚝뚝하고 과묵해보이는 팀은 좋지 않습니다.&lt;/p&gt;
&lt;h4&gt;반복하지 마라&lt;/h4&gt;
&lt;p&gt;반복적인 작업을 팀원들이 하지 않도록 해야합니다. 필요하다면 코드 중복을 관리하는 팀원을 지정합시다.&lt;/p&gt;
&lt;h4&gt;직교성&lt;/h4&gt;
&lt;p&gt;직교적인 조직을 만들어서 각 조직이 맡은 기능을 수행하도록 해야합니다. 팀을 기능적으로 적절히 분리하면 자기 기능에 대해 책임을 지게되고 각 팀들이 변화에 대응할 때 다른 팀에 영향을 주지 않고 상호작용 수를 줄일 수 있습니다. 팀원들의 피로를 줄여줄 수 있습니다.&lt;/p&gt;
&lt;h4&gt;자동화&lt;/h4&gt;
&lt;p&gt;반복되는 작업은 자동화 합시다. 도구 제작자 역할을 지정하는 것도 좋습니다.&lt;/p&gt;
&lt;h4&gt;덧칠을 언제 멈출지 알아라&lt;/h4&gt;
&lt;p&gt;팀은 개인들로 이루어지기 때문에 각 팀원이 자신의 방식을 사용할 수 있도록 해줍시다.&lt;/p&gt;
&lt;h3&gt;42. 유비쿼터스 자동화&lt;/h3&gt;
&lt;p&gt;적극적으로 자동화를 도입합시다. 다음 예시를 참고해서 자동화를 많이 합시다.&lt;/p&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;/li&gt;
&lt;li&gt;빌드 자동화&lt;/li&gt;
&lt;li&gt;최종 빌드 자동화&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;43. 가차 없는 테스트&lt;/h3&gt;
&lt;p&gt;테스트를 가능한 빨리, 그리고 자주, 자동으로 테스트 합시다. 테스트의 중요성은 말 할 필요가 없습니다. 대부분 개발자들은 테스트가 중요하다고 생각합니다. 모두가 중요한 것을 알지만 테스트는 너무 귀찮습니다.&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;그렇지만 귀찮음을 이겨내고 테스트를 가차없이 한다면 큰 이득이 있습니다. 미래에 발생할 수 있는 큰 버그를 막아주고 미리 알 수 있습니다. 여러 단계의 테스트를 활용해서 마치 그물망을 펼치듯이 버그와 오류를 확인합시다.&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;테스트의 종류는 다양합니다. 단위 테스트, 통합 테스트, 성능 테스트 등 다양한 단계가 있습니다. 많은 테스트를 하면 좋지만 매번 직접 수행하기가 힘듭니다. CI/CD 파이프라인을 만들어서 테스트를 자동화해서 자주 돌릴수 있도록 만들면 좋습니다.&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;개인적으로 이번 장에서 가장 중요한 내용은 테스트 자동화라고 생각합니다. 수동으로 하는 테스트는 결국 놓치게 돼있고 실수하게 돼있습니다. 매번 검증할 수 있도록 자동화를 하면 견고한 시스템을 만들 수 있을 것입니다.&lt;/p&gt;
&lt;h3&gt;44. 결국은 모두 글쓰기&lt;/h3&gt;
&lt;p&gt;문서화를 잊지말고 합시다. 문서화는 귀찮은 작업이지만 나중에 도움이 됩니다. 문서화를 할 때 주의할 점은 코드와 문서의 내용이 일치되지 않도록 하는 것입니다. javadoc 주석을 활용하면 코드에 문서화를 같이 할 수 있습니다.&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;글쓰기도 프로그래밍과 비슷한 면이 있어서 dry, 직교성 등의 원칙을 지키면 좋습니다. 예를 들어, 마크다운 형태로 문서를 저장하면 그냥 글로도 볼 수 있고 두레이 같은 협업 도구를 사용하면 PPT 형태 또는 회의 모드로 볼 수 있습니다.&lt;/p&gt;
&lt;h3&gt;45. 위대한 유산&lt;/h3&gt;
&lt;p&gt;클라이언트가 기대하는 시스템을 잘 파악합시다. 결국 소프트웨어는 사용자를 위한 것이므로 사용자가 기대하는 바를 충족시켜주는 것이 최고의 소프트웨어를 만드는 길입니다. 예광탄과 프로토타입을 잘 활용해서 사용자가 기대하는 것을 이끌어냅시다.&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;더 나아가서 사용자가 기대하는 것 이상을 보여줘서 놀라게 합시다. 좋은 평가를 얻을 수 있습니다. 근데 왜 제목이 위대한 유산인건지는 모르겠네요..&lt;/p&gt;
&lt;h3&gt;46. 오만과 편견&lt;/h3&gt;
&lt;p&gt;방어적으로 행동하지 말자. 책임감을 가지고 다른 사람들에게 방어적으로 행동하지 말자.&lt;/p&gt;</description>
      <category>Study/실용주의 프로그래머</category>
      <author>HaeSungDev</author>
      <guid isPermaLink="true">https://juniortech.tistory.com/24</guid>
      <comments>https://juniortech.tistory.com/24#entry24comment</comments>
      <pubDate>Tue, 21 Dec 2021 00:19:18 +0900</pubDate>
    </item>
    <item>
      <title>실용주의 프로그래머 정리 7</title>
      <link>https://juniortech.tistory.com/23</link>
      <description>&lt;h2&gt;프로젝트 전에&lt;/h2&gt;
&lt;p&gt;이번 장에서는 프로젝트를 시작하기 전에 준비해야 할 것들에 대해 다루고 있습니다.&lt;/p&gt;
&lt;h3&gt;36. 요구사항의 구렁텅이&lt;/h3&gt;
&lt;p&gt;대부분의 클라이언트는 원하는 요구사항을 정확히 표현하지 못합니다. 따라서 우리는 클라이언트의 요구사항으로부터 더 정확한 요구사항을 끄집어내야 합니다. 요구사항을 정확하게 뽑아내기 위해서는 사용자가 어떻게 사용하는지에 집중하기 보다는 왜 필요한지 파악하는 것이 더 중요합니다. 요구사항의 핵심적인 내용을 파악해야 추후 요구사항의 변경이 생겼을 때 좀 더 유연하게 대처할 수 있습니다.&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;요구사항을 깊이있게 파악하기 위해 가장 좋은 방법은 사용자가 직접 되는 것입니다. 실제로 사용자와 함께 출근해서 일해보고 도메인을 파악해보면 좀 더 핵심적인 요구사항을 파악할 수 있습니다. &lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;요구사항의 핵심을 파악하고 나면 문서화를 해야하는데 이때는 사용자를 포함 시킨 유스 케이스를 작성해서 표현하는 것이 좋습니다. 그렇지만 너무 상세하게 작성하는 것은 좋지 않습니다. 좋은 요구사항 문서는 추상적이지만 명확하게 작성해야 합니다. 변경될 수 있는 정책은 추상화 속에 감추고 핵심적인 부분은 명확하게 표현하는 것이 좋습니다.&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;조금씩 요구사항이 추가되는 것을 막기 위해 요구사항에 대한 다양한 기록을 추가해보세요. 누가 이 기능을 요구했고 이 요구사항을 개발하는데 얼마나 걸렸는지 등 요구사항에 대한 기록을 추가하면 슬금슬금 요구사항이 추가되는 것을 막을 수 있습니다.&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;마지막으로 공통 용어 사전을 만들고 관리해야 합니다. 같은 의미를 표현하는 여러 용어가 있으면 커뮤니케이션이 복잡해지고 요구사항을 잘못 이해하는 경우도 생깁니다.&lt;/p&gt;
&lt;h3&gt;37. 불가능한 퍼즐 풀기&lt;/h3&gt;
&lt;p&gt;불가능해보이는 문제를 대할 때는 자유의 정도와 애매한 제약 조건을 정확하게 분석하면 문제를 푸는데 큰 도움이 됩니다. 제약 조건의 틀을 파악하는 것만으로도 해결 방안이 나오는 경우도 있습니다. 가장 중요한건 진짜 제약 조건과 헷갈리게 하는 제약 조건을 구별하는 것입니다. &lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;그럼에도 문제가 계속 풀리지 않는다면 한 걸음 물러서서 다음 질문에 대해 답을 찾아봅시다.&lt;/p&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;/li&gt;
&lt;li&gt;반드시 이렇게 해야할까?&lt;/li&gt;
&lt;li&gt;반드시 해야하나?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이 질문들에 답을 찾다보면 갑자기 깨달음을 얻는 경우가 많습니다.&lt;/p&gt;
&lt;h3&gt;38. 준비가 되어야만&lt;/h3&gt;
&lt;p&gt;대부분 사람들이 일의 시작을 미룹니다. 이 때 일을 시작하기 애매해서 기다리는건지 단지 늑장부리는건지 구분할 필요가 있습니다. 이럴 때 좋은 방법은 프로토타이핑을 해보는 것입니다. 가장 어려울 것 같은 부분을 찾아서 프로토타이핑 해보고 만약 큰 문제가 없고 의미없게 느껴진다면 바로 개발을 시작하면 됩니다. 어쩌면 프로토타이핑 중에 전제 조건이 잘못됐다는 것을 파악할 수 도 있습니다. 이는 불필요한 업무 진행을 막아줄 수 있습니다.&lt;/p&gt;
&lt;h3&gt;39. 명세의 함정&lt;/h3&gt;
&lt;p&gt;프로그램 명세화는 요구사항 분석 이후에 개발할 시점까지 작성하는 과정인데 유지보수하기 위한 기록으로 사용할 수 있습니다. 하지만 너무 상세한 명세는 별로 도움이 되지 않습니다. 이해할 수 있게 작성하기는 쉽지 않고 너무 상세한 명세 때문에 개발을 할 때 더 좋은 방법이 있는데 놓칠 가능성도 있습니다. 요구사항 -&amp;gt; 명세 -&amp;gt; 개발을 구분하지 말고 심리스하게 다루도록 합시다. 개발을 하다가 다시 요구사항으로 돌아갈 수 있고 명세가 수정 될 수도 있습니다. 명세에 너무 의존하지 말고 개발하는 과정이라고 생각합시다.&lt;/p&gt;
&lt;h3&gt;40. 동그라미와 화살표&lt;/h3&gt;
&lt;p&gt;형식적인 방법론에 집착하지 말고 유연하게 사용하는 것이 좋습니다. 각 방법론들은 특정 상황에 적합한 경우가 많아서 우리가 진행하는 프로젝트에 딱 들어맞는 경우는 별로 없습니다. 따라서 방법론들을 비판적인 시각으로 잘 바라보고 필요한 부분들만 잘 뽑아서 사용하세요. 방법론의 노예가 되지말고 사용자가 돼야 합니다. 방법론은 정답이 아니고 특정 상황에 맞는 도구일 뿐입니다.&lt;/p&gt;</description>
      <category>Study/실용주의 프로그래머</category>
      <author>HaeSungDev</author>
      <guid isPermaLink="true">https://juniortech.tistory.com/23</guid>
      <comments>https://juniortech.tistory.com/23#entry23comment</comments>
      <pubDate>Mon, 20 Dec 2021 22:31:51 +0900</pubDate>
    </item>
    <item>
      <title>실용주의 프로그래머 정리6</title>
      <link>https://juniortech.tistory.com/22</link>
      <description>&lt;h2&gt;코딩하는 동안 해야 할 일들&lt;/h2&gt;
&lt;p&gt;기계적으로 하는 코딩은 좋지 않은 구조를 만든다. 항상 고민하고 많이 생각하지 않으면 잘못된 프로그램을 만들 가능성이 높아진다. 기계적으로 해도 좋은 코드가 나왔으면 이미 프로그래머는 대체 됐을 것이다. 이번 장에서는 코딩하는 동안 고려해야 될 점들을 알아본다.&lt;/p&gt;
&lt;h3&gt;31. 우연에 맡기는 프로그래밍&lt;/h3&gt;
&lt;p&gt;우연히 운좋게 동작하는 동작에 의존하지 말자. 어떤 원리로 돌아가는지 이해하고 있어야 한다. 또한 다른 사람의 라이브러리를 사용할 때는 문서에 없는 동작에 의존하지 말자. 작성자가 의도하지 않은 동작일 가능성이 높다. 확실하지 않은 사실을 가정하지 말고 증명하자.&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;의도적으로 프로그래밍하는 것이 우연에 맡기는 프로그래밍 보다 훨씬 안전하다. 의도적 프로그래밍을 지키기위한 원칙은 다음과 같다.&lt;/p&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;/li&gt;
&lt;li&gt;노력에 대한 우선순위를 정하자. 가장 중요한 부분에 시간을 투자하자.&lt;/li&gt;
&lt;li&gt;적극적으로 리팩토링 하자&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;32. 알고리즘의 속도&lt;/h3&gt;
&lt;p&gt;알고리즘의 속도를 추정하는 방법 중 빅오 표기법이 유용하다. 알고리즘이 어떤 빅오 표기값을 갖냐에 따라 입력의 크기에 대해 얼마나 속도가 느려질 지 추정할 수 있다. 빅오 표기법으로 알고리즘 속도를 추정하고 느리다면 더 좋은 그래프를 갖는 알고리즘으로 바꿀수 있도록 고민할 수 있다.&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;알고리즘 속도를 추정하는 것도 중요하지만 실제 측정해보는 것이 더 중요하다. 성능에 영향을 주는 알고리즘이라면 실제 수행 속도를 측정해보자.&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;마지막으로 알고리즘 속도가 병목이 아니라면 억지로 최적화 하지 말자. 성급한 최적화는 더 안좋은 결과를 만들 수 있다.&lt;/p&gt;
&lt;h3&gt;33. 리팩토링&lt;/h3&gt;
&lt;p&gt;프로그램은 시간이 지나면서 계속 변화한다. 기능이 추가되고 그 과정에서 이전에 고민했던 설계가 안 맞을 수 도 있고 코드가 지저분해질 수 도 있다. 이때는 망설이지 말고 리팩토링을 하자. 리팩토링은 괴로운 과정이지만 지금 맞는 것이 나중에 맞는 것 보다 덜 아프다. 그냥 빨리 맞자.&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;리팩토링과 항상 따라다니는 것은 일정이다. 일정이 바빠서 리팩토링을 하기 힘든 경우가 많다. 하지만 미래를 위해서 일정을 넉넉하게 잡고 잘 조율할 수 있도록 하는 것이 좋다. 아까 말했듯이 지금 맞는게 덜 아프다..&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;리팩토링 방법에 관해서는 마틴 파울러의 리팩토링 책을 읽어보는 것이 좋다. 여기서는 리팩토링에 가장 중요한 요소만 소개하겠다. 우선 이전에 동작하는 기능에 대한 회귀 테스트를 작성하자. 그 다음 리팩토링을 하면 안심할 수 있다. 리팩토링을 할 때 테스트는 매우 중요하다.&lt;/p&gt;
&lt;h3&gt;34. 테스트하기 쉬운 코드&lt;/h3&gt;
&lt;p&gt;테스트하기 쉬운 코드는 개인적으로 엄청 중요하다고 생각한다. 테스트하기 쉬운 코드를 짜다보면 자연스럽게 좋은 코드를 작성하게 되기 때문이다. 단위 테스트를 작성할 때 객체 또는 메소드가 여러 객체에 의존하고 있으면 테스트하기가 힘들어진다. 이를 좀더 작은 책임으로 분리하면 결국 테스트하기 쉬운 코드가 나오고 재사용 가능하고 유연한 코드를 작성할 수 있다.&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;테스트 코드를 작성할 때는 경계조건과 메소드를 사용하기 위한 계약 조건을 반드시 테스트 해야한다. 그래야 유의미한 테스트가 된다. 보통 버그가 많이 발생하는 곳이 경계 조건과 갖춰야하는 상태이기 때문이다.&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;테스트 코드도 잘 구조화 하는 것이 좋은데 테스트 프레임워크를 사용하는 것이 좋다. 자바에서는 junit을 대부분 쓰기 때문에 junit을 활용해서 테스트를 작성하자. 테스트 코드 작성은 문화다. 팀에서 하고 있지 않으면 쉽게 도입하기 힘들다. 만약 팀이 테스트 코드를 작성하고 있지 않다면 나부터 작성해보도록 하자.&lt;/p&gt;
&lt;h3&gt;35. 사악한 마법사&lt;/h3&gt;
&lt;p&gt;마법사는 복잡한 코드를 쉬운 설정만으로 자동 생성해준다. 매우 유용하지만 한편으로는 안좋은 일이 될 수 있다. 이해하지 못하고 마법사를 사용하면 문제가 발생했을 때 디버깅을 하기 쉽지 않다. 마법사를 사용하려면 마법사가 해주는 일을 이해하자.&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;개인적으로 사용하는 것 중에 스프링 부트가 마법을 굉장히 많이 부린다고 생각한다. 설정하지도 않은 빈을 많이 생성해줘서 편리하게 해준다. 하지만 이런 편리함 속에서 문제가 발생했을 때 잘 해결하기 위해서는 스프링 부트가 결국 어떻게 동작하는지 잘 이해해야 한다. 시간 날때 틈틈이 사용하고 있는 프레임워크를 공부하는 것이 좋을 것 같다.&lt;/p&gt;</description>
      <category>Study/실용주의 프로그래머</category>
      <author>HaeSungDev</author>
      <guid isPermaLink="true">https://juniortech.tistory.com/22</guid>
      <comments>https://juniortech.tistory.com/22#entry22comment</comments>
      <pubDate>Sun, 12 Dec 2021 22:04:56 +0900</pubDate>
    </item>
    <item>
      <title>실용주의 프로그래머 정리5</title>
      <link>https://juniortech.tistory.com/21</link>
      <description>&lt;h2&gt;구부러지거나 부러지거나&lt;/h2&gt;
&lt;p&gt;코드는 항상 빠르게 변화한다. 빠른 변화 속도를 대처하기 위해 느슨하고 유연한 코드를 작성해야 한다. 이번 장에서는 변화하는 세상속에서 코드의 유연성과 적응성을 잃지 않는 법을 소개한다.&lt;/p&gt;
&lt;p&gt;우선 결합도를 낮추는 법을 알아보고, 메타 프로그래밍을 이용해서 적은 양의 코드를 작성해서 유연함을 유지할 수 있는 방법을 소개한다. 그리고 시간 측면에서 결합도를 알아보고 뷰와 모델을 분리하는 법을 소개한다. 마지막으로 모듈들이 데이터를 교환할 수 있는 만남의 장소를 마련해서 결합도를 획기적으로 줄이는 방법을 알아본다.&lt;/p&gt;
&lt;h3&gt;26. 결합도 줄이기와 디미터 법칙&lt;/h3&gt;
&lt;p&gt;결합도를 줄이면 코드가 유연해지고 변경을 잘 적응할 수 있다. 결합도를 낮게 유지하는 방법 중 하나는 디미터 법칙을 지키는 것이다.&lt;/p&gt;
&lt;p&gt;디미터 법칙은 내가 소유한 객체 또는 매개변수로 받은 객체 이외의 메소드는 호출하지 않는 것이다. 예를들어, 메소드 체이닝이 있다. &lt;code&gt;bank.getAccount().getMoney()&lt;/code&gt; 이 코드는 디미터 법칙을 어긴 코드다. account는 현재 소유한 객체가 아니기 때문에 알아서는 안되고 메소드 체이닝으로 호출하게 되면 결합도가 높아진다. 따라서 account의 수정에 영향을 받을수 있다.&lt;/p&gt;
&lt;p&gt;성능이 중요한 상황에서는 디미터 법칙을 어길 수 있다. 하지만 끔찍한 미래를 가져올 수 있기 때문에 가능하면 그러지 않는 것이 좋다.&lt;/p&gt;
&lt;h3&gt;27. 메타 프로그래밍&lt;/h3&gt;
&lt;p&gt;세부 사항은 코드를 지저분하게 만들고 관리하기 힘들게 한다. 추상화 된 코드는 유연성 있고 변경에 강하다. 따라서 코드는 추상화해서 유지할 수 있도록 하고 세부 사항은 메타데이터로 표현하면 유연한 코드를 작성할 수 있을 것이다.&lt;/p&gt;
&lt;p&gt;설정 정보 등의 세부 사항을 코드로 작성하지말고 메타 데이터로 표현하자. 이런 정보들은 언제든지 바뀔 수 있고 바뀔때 마다 다시 컴파일하고 싶지 않을 것이다. 여기에 더해 자주 변화하는 비즈니스 로직도 메타 데이터로 표현할 수 있다. 매우 유연하게 프로그램을 변경할 수 있다.&lt;/p&gt;
&lt;p&gt;보통 프로그램을 재시작할 때 변경된 메타데이터를 읽어서 실행할텐데 이는 유연성이 떨어진다. 실행 중에도 메타 데이터를 변경할 수 있도록 만들자. 더 복잡해지겠지만 매우 강력한 프로그램을 만들 수 있다.&lt;/p&gt;
&lt;h3&gt;28. 시간적 결합&lt;/h3&gt;
&lt;p&gt;시간적 결합을 고려해서 설계하자. 시간적 결합이란 작업의 순서에 의존성이 있는 것이다. 예를들어, A 작업 이후에 B 작업을 하면 B 작업은 A 작업에 대한 시간적 결합이 있는 것이다.&lt;/p&gt;
&lt;p&gt;시간적 결합이 없는 작업들은 동시에 처리될 수 있다. 이는 시간적 결합을 끊은 동시성에 대한 고민으로 이어지고 다양한 설계에 대한 측면을 바라볼 수 있게 된다. 직선적으로 처리할 때는 문제가 없던 것도 동시성을 고려하면 문제가 많아진다. 가장 대표적인 예가 전역 변수다. 동시성을 고려하게 되면 확장 가능성과 성능에 대한 요구사항을 더 쉽게 대비할 수 있다. 또한 자연스럽게 설계가 깔끔해진다.&lt;/p&gt;
&lt;h3&gt;29. 단지 뷰일 뿐이야&lt;/h3&gt;
&lt;p&gt;데이터와 뷰는 따로 관리되는 것이 좋다. 데이터가 뷰를 의존하거나, 뷰가 데이터 구조에 의존하기 시작하면 시간이 지나면 관리하기 힘든 코드가 된다. 또한 한 데이터에 대한 여러가지 뷰의 형태를 보고 싶을때 데이터의 중복이 생기기도 한다.&lt;/p&gt;
&lt;p&gt;따라서 원본 데이터를 따로 관리하고 뷰를 위한 데이터 구조로 바꿔주는 컨트롤러를 두면 유연한 코드 작성이 가능해진다. 새로운 형태의 뷰를 제공하고 싶으면 원본 데이터를 바꿀 필요없이 컨트롤러와 뷰를 추가하면 된다.&lt;/p&gt;
&lt;p&gt;뷰와 데이터를 분리하는 방법에는 여러가지가 있다. 그 중 pub/sub 패턴과 MVC 패턴이 유명하다. pub/sub 패턴은 이벤트의 형태로 뷰와 데이터를 분리한다. 데이터가 변경되면 등록된 뷰에게 이벤트를 통지한다. 만약 관심있는 데이터가 아니라면 구독하지 않으면 된다. MVC 패턴은 아주 익숙한 패턴으로 컨트롤러가 뷰와 데이터 모델을 연결해주는 역할을 한다. 위에서 설명한 것 처럼 컨트롤러가 데이터 모델을 뷰에 맞는 형태로 변환해서 전달한다.&lt;/p&gt;
&lt;p&gt;대부분 좋은 아키텍쳐는 책임을 잘 분리하고 모듈간의 결합도를 떨어뜨리기 위해 나오는 것 같다. 이런 점을 이해하고 있으면 다른 좋은 아키텍쳐를 이해하기 쉬울 것이라고 생각한다. 각 아키텍쳐가 어떤 문제를 해결하기 위해 어떻게 책임을 분리하고 결합을 분리하는지 이해하도록 하자.&lt;/p&gt;
&lt;h3&gt;30. 칠판&lt;/h3&gt;
&lt;p&gt;칠판은 분산된 작업 흐름이 바라보는 데이터 공간에 대한 비유적인 표현이다. 여러 사람이 서로 만나지 않고 칠판에 작업한 데이터를 공유하고 다시 공유된 데이터를 읽고 작업을 진행하는 과정을 생각하면 된다.&lt;/p&gt;
&lt;p&gt;칠판 시스템은 문제의 규모가 크고 복잡한 인공지능 애플리케이션에서 사용할 목적으로 발명됐다. 예를들어 JavaSpaces라는 분산 시스템이있는데 키/값 튜플 공간을 활용한다. 자바 객체를 키값으로 저장하거나 키값으로 가져올 수 있다. Redis와도 유사하다고 생각하면 될 것 같다.&lt;/p&gt;</description>
      <category>Study/실용주의 프로그래머</category>
      <author>HaeSungDev</author>
      <guid isPermaLink="true">https://juniortech.tistory.com/21</guid>
      <comments>https://juniortech.tistory.com/21#entry21comment</comments>
      <pubDate>Mon, 6 Dec 2021 21:47:12 +0900</pubDate>
    </item>
    <item>
      <title>실용주의 프로그래머 정리 4</title>
      <link>https://juniortech.tistory.com/20</link>
      <description>&lt;h2&gt;실용주의 편집증&lt;/h2&gt;
&lt;p&gt;명심하자. 완벽한 소프트웨어는 만들 수 없다. 따라서 소프트웨어를 믿지 말자. 끊임없이 의심하고 방어 코드를 작성하자.(물론 적정선에서)&lt;/p&gt;
&lt;h3&gt;21. 계약에 의한 설계&lt;/h3&gt;
&lt;p&gt;계약에 의한 설계에서는 루틴, 함수, 메소드가 3가지를 지키며 동작하기를 기대한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;선행조건: 함수가 정상적으로 동작하기 위한 조건이다.&lt;/li&gt;
&lt;li&gt;후행조건: 함수가 실행된 이후 만족해야하는 상태(조건)을 의미한다.&lt;/li&gt;
&lt;li&gt;클래스 불변식: 메소드 실행 전, 실행 후에는 특정 조건을 만족해야함을 의미한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;계약에 의한 설계를 지원하는 아이펠같은 언어는 해당 조건을 기술해서 소프트웨어 안정성을 높인다.&lt;/p&gt;
&lt;p&gt;개인적으로 자바에서 인터페이스가 계약이라고 생각한다. 실제로 계약에 의한 설계처럼 강한 조건 검사는 지원하지 않지만 인터페이스가 어떤 기능을 하고 어떻게 동작할 것인지에 대한 계약 역할을 한다고 생각한다. 이는 책임을 명확히 분리해주고 변경에 강한 코드를 작성하도록 도와준다. 리스코프 치환 원칙이 이를 잘 설명해준다.&lt;/p&gt;
&lt;p&gt;책에서 말하는 계약에 의한 설계는 실무에서 사용하기에는 좀 거리가 있다고 느껴졌지만 일부분(인터페이스의 시그니처 처 정도)을 활용하면 좋은 코드를 작성할 수 있을 것이라고 생각한다.&lt;/p&gt;
&lt;h3&gt;22. 죽은 프로그램은 거짓말을 하지 않는다&lt;/h3&gt;
&lt;p&gt;코드에 문제가 있을때 프로그램을 죽이는 것이 차라리 낫다. 정합성이 깨지고 문제가 있는 상태에서 프로그램이 계속 돌아가면 더 큰 문제를 만들 수 있다. &amp;quot;있을수 없는 일이야&amp;quot;라고 추측하지 말고 항상 방어 코드를 만들자. 그리고 있을 수 없는 일이 생기면 프로그램을 종료 시키자.&lt;/p&gt;
&lt;h3&gt;23. 단정적 프로그래밍&lt;/h3&gt;
&lt;p&gt;죽은 프로그램은 거짓말을 하지 않는다에서 말한 것 처럼 &amp;quot;있을 수 없는 일&amp;quot;은 일어날 수 있다. 그리고 있을 수 없는 일이라고 가정하기 때문에 문제가 발생했을 때 더 찾기 힘들다. 이런 문제를 방지하려면 단정문을 사용하는 것이 좋다. 있을 수 없는 상태에 대해 단정문을 설정하고 프로그램을 종료시키자.&lt;/p&gt;
&lt;p&gt;단정문을 사용할 때 주의해야할 점은 부수 효과를 일으키면 안된다는 것이다. 단정문이 부수 효과를 일으키면 더 찾기 힘든 오류를 만들 수 있다. 부수 효과 없이 단정문의 용도로만 단정문을 사용하라.&lt;/p&gt;
&lt;p&gt;다음과 같은 단정문을 피하자. 엘리먼트를 가져오는 부수효과가 발생한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Test.ASSERT(iter.nextElement() != null);&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;24. 언제 예외를 사용할까&lt;/h3&gt;
&lt;p&gt;상황에 따라 예외를 적절히 사용하자. 다만 예외를 특정 정상 흐름의 일부로 사용하지 말자. 더 복잡한 로직을 만들게 될 것이다. 예외는 정말 예외 상황에만 사용하자.&lt;/p&gt;
&lt;p&gt;예외 처리 로직을 따로 분리한 예외 처리기를 사용하는 것도 좋다. 일관된 예외 처리 로직으로 에러를 처리할 수 있다.&lt;/p&gt;
&lt;h3&gt;25. 리소스 사용의 균형&lt;/h3&gt;
&lt;p&gt;리소스를 할당한 주체가 리소스를 해제하도록 하자. 리소스 릭을 방지할 수 있고 버그를 줄일 수 있다. 또한 여러 리소스를 사용한다면 리소스를 할당한 순서의 반대로 해제하도록 하자. 리소스가 엉키는 일을 방지할 수 있다.&lt;/p&gt;
&lt;p&gt;여러 곳에서 동일한 리소스 여러개를 할당하고 해제한다면 리소스 할당 순서를 일치시키자. 교착 상태를 방지할 수 있다. 자바에서 리소스 할당, 해제는 try-with 문을 사용하자. 사용할 수 없는 상황이라면 반드시 finally문에서 리소스를 해제하자.&lt;/p&gt;
&lt;p&gt;리소스에 대한 레퍼런스 카운트를 관리해서 릭이 발생하는 리소스를 탐지하는 방법도 있다. 리소스를 안전하게 관리하고 싶으면 리소스 레퍼런스를 관리해서 점검하도록 하자.&lt;/p&gt;</description>
      <category>Study/실용주의 프로그래머</category>
      <author>HaeSungDev</author>
      <guid isPermaLink="true">https://juniortech.tistory.com/20</guid>
      <comments>https://juniortech.tistory.com/20#entry20comment</comments>
      <pubDate>Sun, 28 Nov 2021 13:00:36 +0900</pubDate>
    </item>
    <item>
      <title>실용주의 프로그래머 정리 3</title>
      <link>https://juniortech.tistory.com/19</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;기본적인 도구&lt;/h2&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;일반 텍스트는 그 자체로 알아보기 쉽고 다루기 쉽다. 데이터를 효율적으로 저장하기 위한 경우를 제외하면 일반 텍스트를 잘 활용하자. 잘 정의된 일반 텍스트는 강력하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;쉘 게임&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉘은 자동화 하기에 정말 좋은 도구다. GUI 기반의 툴들은 제한된 동작을 제공하지만(사실 요즘 GUI들은 플러그인이나 커스터마이징을 많이 지원해준다. 옛날 책이라 그런듯) 쉘을 이용하면 무궁무진한 기능을 제공할 수 있다. 특히 반복적인 작업을 자동화할 때 아주 유용하다. 쉘 스크립트와 명령어를 조합해서 DRY 원칙을 잘 지켜보자&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;h3 data-ke-size=&quot;size23&quot;&gt;소스코드 관리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소스코드 관리는 매우 중요하다. 요즘은 대부분 git을 사용해서 소스코드를 관리한다. 소스코드 버전, 릴리징 관리 등 다양한 기능이 있어서 협업 및 생산성에 큰 도움이 될 뿐만 아니라 문제가 생겼을 때 원인이 되는 코드를 찾을때도 유용하다. 만약 팀에서 버전관리시스템을 쓰고있지 않다면 개인 작업 공간에서만이라도 사용하자. 큰 이득이 있다.&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자기 방어를 위해 코드가 옳다고 생각하지 말자. 코드와 자신을 동일시 하지말자. 코드가 틀렸다고 내가 틀린건 아니다. 코드가 틀리지 않았다는 생각에 갇혀버리면 문제를 제대로 바라보기 힘들다.&lt;/li&gt;
&lt;li&gt;경계 테스트를 하자. 사용자는 개발자가 예상한대로 사용하지 않는다. 따라서 기능에 대한 경계 지점을 잘 테스트하자. 버그를 재현할 수 있을 것이다.&lt;/li&gt;
&lt;li&gt;시각화 툴을 이용해서 데이터를 눈으로 보자. 디버깅할 때 큰 도움이 될 것이다.&lt;/li&gt;
&lt;li&gt;동시성 등 확인하기 힘든 문제가 발생했을 때는 로그 등을 남겨서 추적할 수 있도록 하자.&lt;/li&gt;
&lt;li&gt;고무 오리에게 설명하듯이 버그가 발생한 부분의 구현을 하나하나 설명해보자. 버그가 보일 것이다.&lt;/li&gt;
&lt;li&gt;문제를 좁혀나가자. 문제가 발생한 코드를 좁혀나가면서 버그를 격리하자. 좀 더 찾기 수월해질 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;텍스트 처리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;텍스트 처리 언어를 익히면 생산성에 큰 도움이 된다. ex) awk, sed, 파이썬, perl, ruby, ... 이런 도구들을 사용하면 기존에 사용하던 언어보다 훨씬 빠르게 텍스트를 처리할 수 있을 것이다. 자바 코드의 getter, setter 코드를 자동 삽입해주거나, 데이터베이스 스키마로부터 entity 파일을 생성해주거나 여러가지 것들을 자동화하는데도 쓸 수 있다.&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;</description>
      <category>Study/실용주의 프로그래머</category>
      <author>HaeSungDev</author>
      <guid isPermaLink="true">https://juniortech.tistory.com/19</guid>
      <comments>https://juniortech.tistory.com/19#entry19comment</comments>
      <pubDate>Mon, 22 Nov 2021 23:05:16 +0900</pubDate>
    </item>
    <item>
      <title>실용주의 프로그래머 정리 2</title>
      <link>https://juniortech.tistory.com/18</link>
      <description>&lt;h2&gt;실용주의 접근법&lt;/h2&gt;
&lt;p&gt;소프트웨어 개발에서 공리와 같은 아이디어와 보편화된 프로세스들을 다룬다. 이 장에 있는 내용들을 잘 숙지하고 활용하면 깔끔하고 변경에 강한 코드를 작성하면서 프로젝트를 성공적으로 완수할 수 있을 것이다.&lt;/p&gt;
&lt;h3&gt;7. 중복의 해악&lt;/h3&gt;
&lt;p&gt;DRY(Don&amp;#39;t Repeat Yourself) 원칙은 프로그래머에게 가장 중요한 원칙 중 하나이다. 중복이 불가피한 상황도 있을 수 있지만 대부분은 중복을 제거할 수 있다. 지금 당장 편하자고 중복을 만들지 말고 중복을 없애자.&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;중복은 쌓이다보면 끔찍한 결과를 가져온다. 중복을 피하자!&lt;/p&gt;
&lt;h3&gt;8. 직교성&lt;/h3&gt;
&lt;p&gt;직교성은 모듈, 컴포넌트 등이 서로 의존성이 없는 것을 의미한다. 프로그램을 구성하는 컴포넌트 중 한 컴포넌트의 코드가 변경됐을 때 다른 컴포넌트는 전혀 변경할 필요가 없으면 직교적인 프로그램이라고 생각할 수있다.&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;직교성을 잘 지킨 시스템은 기능 추가나 변경이 쉬워진다. 서로 영향이 없기 떄문에 버그 발생 가능성도 낮다. 또한 각 컴포넌트를 테스트하기 쉬워지므로 더욱 견고한 시스템이 된다. 결과적으로 직교성을 활용하면 생산성을 향상 시킬 수 있고 시스템에 내재돼 있는 리스크도 감소 시킬 수 있다.&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;이러한 직교성은 코드나 시스템 뿐만 아니라 프로젝트 팀, 문서화에도 큰 도움을 준다. 직교성을 잘 지킨 조직은 회의 참여 인원이 줄어서 의사결정을 빠르게 할 수 있다. 직교성을 잘 지킨 문서는 스타일을 변경하기 쉬워진다. &lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;이처럼 직교성은 개발뿐만 아니라 여러 부분에서 중요한 원칙으로 활용할 수 있다. 직교성을 잘 지키자!&lt;/p&gt;
&lt;h3&gt;9. 가역성&lt;/h3&gt;
&lt;p&gt;엔지니어들은 문제에 대해 단순한 하나의 해결안을 생각하고 좋아한다. 하지만 프로젝트에 대한 결정은 항상 변화하고 쉽게 뒤집히고 해당 방법이 쓸모 없어질 수도있다. 가역성을 잘 지킬수 있도록 고민하자.&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;가역성은 초기 상황으로 되돌아 올 수 있는 성질을 의미한다. 프로젝트에 대한 결정이 변화했을 때 이전 상황으로 빠르게 돌아가서 변경 사항을 반영할 수 있도록 유연한 아키텍쳐를 구성하자.&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;세부 사항을 잘 격리하고 인터페이스를 정의해서 변경에 대한 영향도를 줄이자. 시스템 배포 방식이 어떻게 될지 확신이 없으면 설정 변경만으로 배포 방식을 바꿀수 있도록 고민하자. &lt;/p&gt;
&lt;h3&gt;10. 예광탄&lt;/h3&gt;
&lt;p&gt;예광탄은 총을 쏠때 빛의 궤적을 보여주는 탄의 종류다. 이를 통해 총알의 궤적을 눈으로 바로 보고 방향을 수정할 수 있다. 실용주의 프로그래머들은 예광탄 사용을 선호한다.&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;새로 진행해야하는 프로젝트에 대해 감이 안오는 상황에서 예광탄을 사용하면 좋다. 프로젝트의 한 기능만 구현을 해서 제대로 동작하는지 증명할 수 있다. 만약 적합하지 않다면 수정할 수 있다. 기능 하나만 구현했기 때문에 수정하기가 수월하다.&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;이런 방식은 사용자들도 선호한다. 전체 기능은 아니지만 바로 사용할 수 있는 프로그램을 눈으로 볼 수 있고 자기가 원하던 방식인지 바로 피드백을 줄 수 있다. 또한 프로젝트에 참여하는 개발자들도 선호한다. 기본적인 프로젝트의 구조가 잡혀있어서 개발을 쉽게 시작할 수 있다.&lt;/p&gt;
&lt;h3&gt;11. 프로토타입과 포스트잇&lt;/h3&gt;
&lt;p&gt;실제 제품을 개발하기 전에 특정 측면이 걱정된다면 프로토타입을 만들어서 확인해보자. 알고리즘의 성능이 괜찮을지, 아키텍처 구성이 괜찮을지, 외부 데이터의 구조를 적용할 수 있을지 등등 다양한 측면에서 활용이 가능하다.&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;프로토타입은 코드로 만들 수 도 있고 단순히 포스트잇을 붙여가면서 만들수 도 있다. 증명하고자 하는 특정 측면에 적합한 도구를 사용하자. 프로토타입을 만들때 중요한 점은 증명을 한 후에는 프로토타입을 버려야한다는 것이다. 실제 제품에 사용하는 것이 아니라 테스트 및 증명을 위해 사용해야 한다. 실제 제품에 사용하고 싶으면 예광탄을 사용하자.&lt;/p&gt;
&lt;h3&gt;12. 도메인 언어&lt;/h3&gt;
&lt;p&gt;언어는 문제에 대해 생각하는 방식과 의사소통에 영향을 미친다. 따라서 해결하고자 하는 문제 도메인에 특정 언어로 접근하게 되면 제한이 생길 수 있다. 이럴때는 도메인 언어를 만들어서 문제 도메인에 적합한 표현 방식을 고민해보자. 의사소통을 원할하게 도와주고 새로운 문제 해결 방법을 제시할 수 도 있다.&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;소형 언어를 만들어도 좋고 단순히 라인으로 도메인을 표현해도 좋다. 도메인 언어를 만드는 시간에 대한 보상은 나중에 배로 돼서 돌아올 것이다.&lt;/p&gt;
&lt;h3&gt;13. 추정&lt;/h3&gt;
&lt;p&gt;추정을 잘하면 무언가의 가능성을 판단하는 능력이 생긴다. 추정 능력을 키우자.&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;추정의 단위는 정확도를 의미한다. 상황에 따라 다른 단위를 사용할 수 있다. 예를들어 130일과 6개월은 다른 느낌을 준다. 130일은 좀 더 정확한 수치처럼 느껴진다. 단위를 고려해서 추정하자. 추정을 정확하게 하는 가장 좋은 방법은 이미 비슷한 일을 해본 사람을 찾는 것이다. 좋은 추정치를 내줄 수 있을 것이다. 추정을 원하는 상대가 무엇을 묻는지 이해하자. 그러면 추정의 정확도뿐만 아니라 도메인의 범위까지 감을 잡을 수 있을 것이다.&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;시스템 모델을 만들고 컴포넌트로 나누자. 그런 다음 각 매개변수의 값을 주면 추정치를 계산할 수 있을 것이다.&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;프로젝트 일정을 추정할 때는 바로 판단하지 말자. 프로젝트의 단계를 일부분 반복하고 그 결과를 가지고 추정하자.&lt;/p&gt;</description>
      <category>Study/실용주의 프로그래머</category>
      <author>HaeSungDev</author>
      <guid isPermaLink="true">https://juniortech.tistory.com/18</guid>
      <comments>https://juniortech.tistory.com/18#entry18comment</comments>
      <pubDate>Sun, 21 Nov 2021 19:10:53 +0900</pubDate>
    </item>
    <item>
      <title>실용주의 프로그래머 정리1</title>
      <link>https://juniortech.tistory.com/17</link>
      <description>&lt;p&gt;실용주의 프로그래머를 읽고 간단하게 정리한 내용입니다. 문제가 될 시 삭제하겠습니다.&lt;/p&gt;
&lt;h2&gt;서문&lt;/h2&gt;
&lt;p&gt;서문에서는 다른 내용보다는 실용주의 프로그래머의 가장 기본적인 특징에 관한 내용이 기억에 남았다. 자신의 일에 관심과 애정을 가지고 자신의 일에 대해 생각하면서 일하라는 내용으로 특히 두번째 특징이 인상 깊었다.&lt;/p&gt;
&lt;p&gt;우리는 보통 관성적으로 일을 해나간다. 새로운 일을 처음 할 때는 고민을 하면서 효율적인 방식을 찾지만 시간이 지나면 반복적으로 일을 한다. 심지어 새로운 일도 기존 방식대로 하는 경우가 많다.&lt;/p&gt;
&lt;p&gt;실용주의 프로그래머는 자기가 하는 일에 대해 관심을 많이 가지고 더 효율적인 방법이 있을지 고민한다. 이것이 지속적으로 반복되면 나중에는 실력있는 개발자가 될 수 있을 것이라고 생각한다. 항상 하는 일에 대해 관심을 갖고 자동화할 수 있는 일이 있을지, 더 좋은 방법이 있을지 고민하자.&lt;/p&gt;
&lt;h2&gt;실용주의 철학&lt;/h2&gt;
&lt;h3&gt;1. 고양이가 내 소스코드를 삼켰어요&lt;/h3&gt;
&lt;p&gt;무지나 실수를 인정하기는 쉽지 않다. 그래서 변명을 만들고 다른 무언가를 탓한다. 이는 사실 다른 사람이 들었을 때 좋지 않게 보이고 무책임하게 보인다. 차라리 책임을 지고 문제를 해결할 수 있는 대안을 제시하자. 변명을 만드는 것은 고양이가 내 소스코드를 삼켰다고 고양이 탓을 하는 것과 마찬가지다. 바보같이 변명하지 말고 자신의 실수에 대해 책임지고 대안을 제시하자.&lt;/p&gt;
&lt;h3&gt;2. 소프트웨어 엔트로피&lt;/h3&gt;
&lt;p&gt;엔트로피는 무질서한 정도를 나타내는 물리학 용어고 엔트로피는 시간이 지날수록 점점 증가한다. 소프트웨어 개발도 유사하다. 시간이 지나면 엔트로피가 증가한다.&lt;/p&gt;
&lt;p&gt;엔트로피가 증가하는 것을 완전히 막기는 힘들지만 지연 시킬수는 있다. 쓰이지 않는 코드, 안좋은 코드를 계속 정리하자. 이런 코드들은 다른 사람들 또는 나에게 또다른 지저분한 코드를 만들기 쉽게 해준다. 마음을 아주 편하게 해준다. 코드를 자주 정리하자.&lt;/p&gt;
&lt;h3&gt;3. 돌멩이 수프와 삶은 개구리&lt;/h3&gt;
&lt;p&gt;돌멩이 수프와 삶은 개구리는 비유적인 이야기다 여기서 설명하기엔 정리가 길어질 것 같아서 생략하겠다.&lt;/p&gt;
&lt;p&gt;돌맹이 수프 이야기 비유는 간단히 얘기하면 변화를 이끌어 내는 방법이다. 변화를 싫어하는 사람들에게 크게 힘들이지 않고 보여줄 수 있는 만큼만 보여주자. 그리고 끝에 가볍게 한마디 붙여서 변화를 이끌자. &amp;quot;물론 만약.. 이렇게 하면 더 좋겠죠&amp;quot;&lt;/p&gt;
&lt;p&gt;삶은 개구리 이야기 비유는 반대로 변화를 감지하지 못하고 안좋은 상황을 만나는 내용이다. 프로젝트를 큰 그림으로 보고 주의하자. 안좋은 변화가 잃어나고 있는지 가끔씩 생각하며 현재 상황을 크게 보자.&lt;/p&gt;
&lt;h3&gt;4, 적당히 괜찮은 소프트웨어&lt;/h3&gt;
&lt;p&gt;개발자는 요구사항을 받아서 원하는 소프트웨어를 만드는 직업이다. 개발자들은 기술에 집중한 나머지 이를 잊고 완벽한 소프트웨어를 만들기 위해 시간을 많이 쓰는 개발자들이 있다. 하지만 완벽한 소프트웨어는 없다는 걸 기억하자.&lt;/p&gt;
&lt;p&gt;사용자가 원하는 것은 빨리 사용할 수 있게 되는 것이지 완벽한 소프트웨어를 쓰는게 아니다. 하지만 느리고 형편없는 소프트웨어를 쓰기를 원하는 것도 아니니 적당히 괜찮은 소프트웨어를 만들 수 있도록 노력하자. 사용자와 요구사항을 협의할 때 품질을 포함시키는 것도 좋은 방법이다.&lt;/p&gt;
&lt;p&gt;오버 엔지니어링을 경계하고 적당한 수준의 소프트웨어를 만들 수 있도록 하자.&lt;/p&gt;
&lt;h3&gt;5. 지식 포트폴리오&lt;/h3&gt;
&lt;p&gt;투자자들은 자산 포트폴리오를 구성하고 주기적으로 문제가 있는지 파악하고 바꾼다. 이를 통해 수익을 극대화할 수 있도록 노력한다. 리스크 관리, 다각화, 검토 및 재조정 등을 이용해서 자산 포트폴리오를 계속 바꾼다.&lt;/p&gt;
&lt;p&gt;이와 유사하게 지식 포트폴리오를 구성하는 프로그래머들도 있다. 끊임없이 공부하고 트렌드를 파악하고 자신의 지식 포트폴리오를 업그레이드한다. 이렇게 꾸준히 지식 포트폴리오를 관리하면 시장에서 계속 원하는 프로그래머가 될 수 있을 것이다.&lt;/p&gt;
&lt;p&gt;지금부터 조금씩 노력해가자. 매년 새로운 언어를 배우고 기술 서적을 읽고 다양한 경험을 하자.&lt;/p&gt;
&lt;h3&gt;6. 소통하라&lt;/h3&gt;
&lt;p&gt;프로그래머는 생각보다 커뮤니케이션이 중요한 직업이다. 따라서 상대방에게 말하고자 하는 내용을 잘 전달하는 것이 중요하다.&lt;/p&gt;
&lt;p&gt;말하고 싶은게 무엇인지 파악하고, 듣는 사람이 원하는 지식을 이용해서 말하고, 적당한 말할 때를 고르자. 또한 상대방의 스타일을 파악하고 원하는 방식으로 전달하자. 이 외에도 수많은 소통 기술을 익히고 연습하자.&lt;/p&gt;
&lt;p&gt;소통은 쉽지 않지만 일을 매우 중요한 스킬이다. 기술에만 관심을 갖지말고 소통에도 계속 관심을 갖자&lt;/p&gt;</description>
      <category>Study/실용주의 프로그래머</category>
      <author>HaeSungDev</author>
      <guid isPermaLink="true">https://juniortech.tistory.com/17</guid>
      <comments>https://juniortech.tistory.com/17#entry17comment</comments>
      <pubDate>Wed, 17 Nov 2021 20:26:43 +0900</pubDate>
    </item>
  </channel>
</rss>