<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>공부 &amp;mdash; The Heartade Space</title>
    <link>https://blog.heartade.dev/tag:공부</link>
    <description>life as a continuous loop of building and breaking</description>
    <pubDate>Fri, 01 May 2026 11:43:58 +0000</pubDate>
    <item>
      <title>리팩터링 2판 스터디: 8장 &#39;기능 이동&#39;</title>
      <link>https://blog.heartade.dev/ripaegteoring-2pan-seuteodi-8jang-gineung-idong</link>
      <description>&lt;![CDATA[이 글은 리팩터링 2판 스터디 시리즈 중 하나입니다.&#xA;&#xA;리팩터링 2판 스터디의 네 번째 글입니다. 여러 명이 나눠서 정리하는 스터디였기 때문에 이 글을 마지막으로 스터디는 끝나는데, 나머지 내용도 제 블로그에 정리할지는 좀 고민해 봐야겠어요.&#xA;&#xA;이 장은 말 그대로 코드를 옮겨야 하는 상황과 그 방법에 대해 다룹니다. 절차적 프로그래밍에서 코드를 절차상 적절한™ 위치로 옮기는 건 아주 중요하죠.&#xA;!--more--&#xA;&#xA;목차&#xA;&#xA; 정리&#xA;   8.1 함수 옮기기&#xA;   8.2 필드 옮기기&#xA;   8.3 문장을 함수로 옮기기&#xA;   8.4 문장을 호출한 곳으로 옮기기&#xA;   8.5 인라인 코드를 함수 호출로 바꾸기&#xA;   8.6 문장 슬라이드하기&#xA;   8.7 반복문 쪼개기&#xA;   8.8 반복문을 파이프라인으로 바꾸기&#xA;   8.9 죽은 코드 제거하기&#xA;&#xA;정리&#xA;&#xA;8.1 함수 옮기기&#xA;&#xA;  모듈성을 높이려면 서로 연관된 요소들을 함께 묶고, 요소 사이의 연결 관계를 쉽게 찾고 이해할 수 있도록 해야 한다. 하지만 프로그램을 얼마나 잘 이해했느냐에 따라 구체적인 방법이 달라질 수 있다. 보통은 이해도가 높아질수록 소프트웨어 요소들을 더 잘 묶는 새로운 방법을 깨우치게 된다. 그래서 높아진 이해를 반영하려면 요소들을 이리저리 옮겨야 할 수 있다. - p.278&#xA;&#xA;절차 (p.278)&#xA;&#xA;선택한 함수가 현재 컨택스트에서 사용 중인 모든 프로그램 요소를 살펴본다. 이 요소들 중에도 함께 옮겨야 할 게 있는지 고민해본다.&#xA;선택한 함수가 다형 메서드인지 확인한다.&#xA;   객체 지향 언어의 다형성(polymorphism)을 구현하는 메서드인지 확인한다는 의미입니다. 슈퍼클래스나 서브클래스에도 선언된 메서드라면 전부 같이 옮겨 줘야 하니까요.&#xA;선택한 함수를 타깃 컨텍스트로 복사한다(이 때 원래의 함수를 소스 함수라 하고 복사해서 만든 새로운 함수를 타깃 함수라 한다). 타깃 함수가 새로운 터전에 잘 자리잡도록 다듬는다.&#xA;정적 분석을 수행한다.&#xA;소스 컨텍스트에서 타깃 함수를 참조할 방법을 찾아 반영한다.&#xA;소스 함수를 타깃 함수의 위임 함수가 되도록 수정한다.&#xA;테스트한다.&#xA;소스 함수를 인라인(6.2절)할지 고민해본다.&#xA;&#xA;요점&#xA;&#xA; 여러 개의 함수를 함께 옮기는 경우, 호출하는 함수보다 호출되는 함수를 먼저 옮기는 게 낫습니다.&#xA;   원문에 &#34;하위 함수들의 호출자가 고수준 함수 하나뿐이면 먼저 하위 함수들을 고수준 함수에 인라인한 다음...&#34;이라는 내용이 있는데, 여기서 말하는 고수준 함수가 Higher-Order Function을 뜻하는 것인지 아니면 그냥 함수의 호출 관계를 하위/상위로 나눈 것인지 모르겠네요. 원서를 읽어 봐야 이해가 될 것 같습니다...&#xA; 여러 개의 함수를 함께 옮기는 경우, 코드의 다른 곳에 미치는 영향이 적은 함수를 먼저 옮깁시다.&#xA; 옮겨지는 함수가 원래 있던 곳(소스 컨텍스트)의 요소들을 사용한다면 그 요소들을 매개변수로 넘기거나 컨텍스트 자체를 참조(레퍼런스)로 넘겨 줍시다.&#xA;   컨텍스트 자체를 넘겨 준다는 것의 예를 들자면, SomeClass.method()를 OtherClass로 옮기고 싶은데 method의 구현에 SomeClass의 필드들을 참조해야 한다면 아예 OtherClass.method(someClass) 식으로 SomeClass의 인스턴스 참조를 넘길 수도 있다는 의미입니다.&#xA; 옮겨지는 함수를 호출하는 코드를 모두 한 번에 찾아서 바꾸는 게 아니라, 함수의 구현만 옮기고 기존 선언을 위임 함수(새로운 구현을 호출하는 함수)로 남겨두는 점이 특기할 만합니다. 이 위임 함수를 호출하는 코드를 직접 호출로 바꿀지는 마지막에 고민해도 괜찮으니 한 번에 최소한의 수정만 하자는 것이죠.&#xA;&#xA;8.2 필드 옮기기&#xA;&#xA;  필드 옮기기 리팩터링은 대체로 더 큰 변경의 일환으로 수행된다. 예컨대 필드 하나를 잘 옮기면, 그 필드를 사용하던 많은 코드가 원래 위치보다 옮겨진 위치에서 사용하는 게 더 수월할 수 있다. 그렇다면 리팩터링을 마저 진행하여 호출 코드들까지 모두 변경한다. - p.290&#xA;&#xA;절차 (p.290)&#xA;&#xA;소스 필드가 캡슐화되어 있지 않다면 캡슐화한다.&#xA;테스트한다.&#xA;타깃 객체에 필드(와 접근자 메서드들)를 생성한다.&#xA;정적 검사를 수행한다.&#xA;소스 객체에서 타깃 객체를 참조할 수 있는지 확인한다.&#xA;   참조할 수 없으면 참조하게 만듭시다(...)&#xA;접근자들이 타깃 필드를 사용하도록 수정한다.&#xA;테스트한다.&#xA;소스 필드를 제거한다.&#xA;테스트한다.&#xA;&#xA;요점&#xA;&#xA; 크게 타깃 필드 만들기, 소스 컨텍스트의 접근자 수정하기, 소스 필드 없애기의 세 단계로 나눌 수 있겠습니다.&#xA;   그런데 소스의 접근자는 그대로 둡니다. 이 시점에서 소스 컨텍스트의 접근자는 일종의 위임 함수가 된 상태인 셈인데, 위임 함수를 인라인할지 고민하는 건 별개의 문제라고 보는 것 같아요.&#xA; 여러 소스에서 같은 타깃을 공유한다면(즉 소스 객체가 타깃 객체를 다대일 관계로 참조한다면), 우선 소스 필드를 남겨둔 상태로 세터가 타깃 필드와 소스 필드 모두를 갱신하게 한 다음 어서션(10.6절)을 통해 일관성을 깨뜨리는 갱신을 검출해 봅시다.&#xA;&#xA;8.3 문장을 함수로 옮기기&#xA;&#xA;  중복 제거는 코드를 건강하게 관리하는 가장 효과적인 방법 중 하나다. 예컨대 특정 함수를 호출하는 코드가 나올 때마다 그 앞이나 뒤에서 똑같은 코드가 추가로 실행되는 모습을 보면, 나는 그 반복되는 부분을 피호출 함수로 합치는 방법을 궁리한다. - p.296&#xA;&#xA;절차 (p.297)&#xA;&#xA;반복 코드가 함수 호출 부분과 멀리 떨어져 있다면 문장 슬라이드하기(8.6절)를 적용해 근처로 옮긴다.&#xA;타깃 함수를 호출하는 곳이 한 곳뿐이면, 단순히 소스 위치에서 해당 코드를 잘라내어 피호출 함수로 복사하고 테스트한다. 이 경우라면 나머지 단계는 무시한다.&#xA;호출자가 둘 이상이면 호출자 중 하나에서 &#39;타깃 함수 호출 부분과 그 함수로 옮기려는 문장들을 함께&#39; 다른 함수로 추출(6.1절)한다. 추출한 함수에 기억하기 쉬운 임시 이름을 지어준다.&#xA;다른 호출자 모두가 방금 추출한 함수를 사용하도록 수정한다. 하나씩 수정할 때마다 테스트한다.&#xA;모든 호출자가 새로운 함수를 사용하게 되면 원래 함수를 새로운 함수 안으로 인라인(6.2절)한 후 원래 함수를 제거한다.&#xA;새로운 함수의 이름을 원래 함수의 이름으로 바꿔 준다(함수 이름 바꾸기, 6.5절).&#xA;   더 나은 이름이 있다면 그 이름을 쓴다.&#xA;&#xA;요점&#xA;&#xA; 단순히 원래 있던 함수 안으로 코드를 옮기는 게 아니라, 원래 함수와 함수 안으로 옮길 코드를 함께 호출하는 새로운 함수를 만들고, 각각의 호출을 이 새 함수로 바꿔 가며 테스트한 다음, 그제서야 문제가 없으면 원래 함수의 내용을 새 함수 안으로 옮깁니다. 이렇게 하면 반복 코드의 미묘한 차이로 문제가 생기는 상황을 잡아낼 수 있습니다.&#xA; 완전히 하나의 함수로 합칠 수 있는 상황은 아니지만 여전히 해당 함수와 함께 사용되어야만 하는 코드라면 (예를 들어 어떤 코드가 어느 함수를 호출할 때만 사용되지만, 그 함수를 호출할 때마다 그 코드를 사용하는 것은 아니라면) 함수 안으로 옮기는 대신 위 절차의 4번까지만 수행해서 새로운 함수로 만듭니다.&#xA;&#xA;8.4 문장을 호출한 곳으로 옮기기&#xA;&#xA;앞 절의 반대입니다. 앞 절에서는 어떤 함수를 호출할 때 함수 밖에서 항상 동일한 동작을 수행한다면 그 동작을 함수 안으로 옮기는 방식이었는데, 반대로 어떤 함수를 호출할 때 그 함수 안의 한 부분이 경우에 따라 다르게 동작해야 한다면 그 부분을 함수 밖으로 꺼냅니다.&#xA;&#xA;절차 (p.302)&#xA;&#xA;호출자가 한두 개뿐이고 피호출 함수도 간단한 단순한 상황이면, 피호출 함수의 처음(혹은 마지막) 줄(들)을 잘라내어 호출자(들)로 복사해 넣는다(필요하면 적당히 수정한다). 테스트만 통과하면 이번 리팩터링은 여기서 끝이다.&#xA;더 복잡한 상황에서는, 이동하지 &#39;않길&#39; 원하는 모든 문장을 함수로 추출(6.1절)한 다음 검색하기 쉬운 임시 이름을 지어준다.&#xA;원래 함수를 인라인(6.2절)한다.&#xA;추출된 함수의 이름을 원래 함수의 이름으로 변경한다(함수 이름 바꾸기, 6.5절).&#xA;   더 나은 이름이 있다면 그 이름을 쓴다.&#xA;&#xA;요점&#xA;&#xA; &#39;이동하지 않길 원하는 문장을 추출&#39;하는 상황에서, 대상 함수가 다형 메서드라면 모든 구현에서 남길 부분을 메서드로 추출한 다음 (이 부분은 당연히 모든 구현에서 동일해야 합니다) 슈퍼클래스의 메서드만 남깁니다. 이 때 남길 부분은 모든 구현에서 동일한 부분이어야 합니다.&#xA;&#xA;8.5 인라인 코드를 함수 호출로 바꾸기&#xA;&#xA;  이미 존재하는 함수와 똑같은 일을 하는 인라인 코드를 발견하면 보통은 해당 코드를 함수 호출로 대체하길 원할 것이다. 예외가 있다면 [...] 기존 함수의 코드를 수정하더라도 인라인 코드의 동작은 바뀌지 않아야 할 때뿐이다. 이 경우인가를 판단하는 데는 함수 이름이 힌트가 된다. 이름을 잘 지었다면 인라인 코드 대신 함수 이름을 넣어도 말이 된다. - p.308&#xA;&#xA;절차 (p.309)&#xA;&#xA;인라인 코드를 함수 호출로 대체한다.&#xA;테스트한다.&#xA;&#xA;요점&#xA;&#xA; 이 절은 절차가 수상하게 단순한데, 인라인 코드가 이미 다른 함수에 구현된 일을 한다는 것이 명확한 상황에서 그 코드를 함수로 대체하는 일에 대한 내용이기 때문입니다. 그냥 바퀴를 재발명하지 말자는 내용입니다.&#xA; 자주 사용되는 코드가 있고, 그 코드를 라이브러리 함수나 기존에 구현된 다른 함수로 대체할 수 없다면 함수 추출하기(6.1절)를 사용해서 함수로 만들어 줍시다.&#xA;&#xA;8.6 문장 슬라이드하기&#xA;&#xA;  관련 코드끼리 모으는 작업은 다른 리팩터링(주로 함수 추출하기; 6.1절)의 준비 단계로 자주 행해진다. 관련 있는 코드들을 명확히 구분되는 함수로 추출하는 게 그저 문장들을 한데로 모으는 것보다 나은 분리법이다. 하지만 코드들이 모여 있지 않다면 함수 추출은 애초에 수행할 수조차 없다. - p.310&#xA;&#xA;절차 (p.311)&#xA;&#xA;코드 조각(문장들)을 이동할 목표 위치를 찾는다. 코드 조각의 원래 위치와 목표 위치 사이의 코드들을 훑어보면서, 조각을 모으고 나면 동작이 달라지는 코드가 있는지 살핀다.&#xA;   다음과 같은 간섭이 있다면 포기한다.&#xA;     코드 조각에서 참조하는 요소를 선언하는 문장 앞으로는 이동할 수 없다.&#xA;     코드 조각을 참조하는 요소의 뒤로는 이동할 수 없다.&#xA;     코드 조각에서 참조하는 요소를 수정하는 문장을 건너뛰어 이동할 수 없다.&#xA;     코드 조각이 수정하는 요소를 참조하는 요소를 건너뛰어 이동할 수 없다.&#xA;코드 조각을 원래 위치에서 잘라내어 목표 위치에 붙여넣는다.&#xA;테스트한다.&#xA;&#xA;요점&#xA;&#xA; 이 책에서 가장 자주 언급되는 리팩터링 기법 중 하나인 것 같은데, 말 그대로 코드 블록을 위아래로 옮기는 것입니다. VS Code에서 Alt+↑ 아니면 Alt+↓로 하는 그거죠.&#xA; 절차에서 &#39;다음과 같은 간섭&#39;을 언급하는데, 간단히 요약하자면 코드의 인과 관계를 유지하는 선에서 옮겨야 한다는 것입니다.&#xA; 한 문장만 옮길 때는 &#39;문장 교환하기(Swap Statement)&#39;라고 부른다고 하네요.&#xA;&#xA;8.7 반복문 쪼개기&#xA;&#xA;  종종 반복문 하나에서 두 가지 일을 수행하는 모습을 보게 된다. 그저 두 일을 한꺼번에 처리할 수 있다는 이유에서 말이다. 하지만 이렇게 하면 반복문을 수정해야 할 때마다 두 가지 일 모두를 잘 이해하고 진행해야 한다. 반대로 각각의 반복문으로 분리해 두면 수정할 동작 하나만 이해하면 된다. - p.316&#xA;&#xA;절차 (p.317)&#xA;&#xA;반복문을 복제해 두 개로 만든다.&#xA;반복문이 중복되어 생기는 부수효과를 파악해서 제거한다.&#xA;테스트한다.&#xA;완료됐으면, 각 반복문을 함수로 추출(6.1절)할지 고민해본다.&#xA;&#xA;요점&#xA;&#xA; 이 리팩터링은 각 반복문을 다른 함수로 추출해야 할지 고민해 보는 것으로 마무리됩니다. 하나의 반복문 안에서 여러 일이 일어나고 있어서 쪼개야 한다면, 그 함수가 여러 기능을 수행하고 있다는 신호일 수 있기 때문입니다.&#xA; 본문에서는 한 문단을 할애하여 최적화를 할 거면 리팩터링을 먼저 하고 하라고 호소하고 있습니다. &#39;반복문 하나에서 처리할 수 있는 일을 왜 굳이 쪼개지?!&#39; 라는 생각이 들 때가 있긴 하지만, 미래의 우리는 그 반복문의 역할을 까먹을 것이므로 유지보수를 위해 역할별로 쪼개어 주는 것이 아무래도 좋겠습니다.&#xA;&#xA;8.8 반복문을 파이프라인으로 바꾸기&#xA;&#xA;  논리를 파이프라인으로 표현하면 이해하기 훨씬 쉬워진다. 객체가 파이프라인을 따라 흐르며 어떻게 처리되는지를 읽을 수 있기 때문이다. - p.320&#xA;&#xA;절차 (p.320)&#xA;&#xA;반복문에서 사용하는 컬렉션을 가리키는 변수를 하나 만든다.&#xA;반복문의 첫 줄부터 시작해서, 각각의 단위 행위를 적절한 컬렉션 파이프라인 연산으로 대체한다. 이때 컬렉션 파이프라인 연산은 1. 에서 만든 반복문 컬렉션 변수에서 시작하여, 이전 연산의 결과를 기초로 연쇄적으로 수행된다. 하나를 대체할 때마다 테스트한다.&#xA;반복문의 모든 동작을 대체했다면 반복문 자체를 지운다.&#xA;&#xA;요점&#xA;&#xA; filter, map, reduce 같은 컬렉션 파이프라인을 쓰자는 내용입니다. 아직 안 쓰고 계셨다면 쓰세요. 두 번 쓰세요.&#xA;&#xA;8.9 죽은 코드 제거하기&#xA;&#xA;  코드가 더 이상 사용되지 않게 됐다면 지워야 한다. 혹시 다시 필요해질 날이 오지 않을까 걱정할 필요 없다. 우리에겐 버전 관리 시스템이 있다! - p.327&#xA;&#xA;절차 (p.328)&#xA;&#xA;죽은 코드를 외부에서 참조할 수 있는 경우라면(예컨대 함수 하나가 통째로 죽었을 때) 혹시라도 호출하는 곳이 있는지 확인한다.&#xA;없다면 죽은 코드를 제거한다.&#xA;테스트한다.&#xA;&#xA;요점&#xA;&#xA; 안 쓰는 코드를 주석 처리하지 말고 지우라고 해서 좀 찔립니다. 깃을 열심히 써야겠어요.&#xA;&#xA;---&#xA; 태그: #리팩터링 #공부 #스터디&#xA;&#xA;!--more--&#xD;&#xA;---&#xD;&#xA;Dani Soohan Park (@heartade)&#xD;&#xA;&#xD;&#xA;Follow this blog at Fediverse:&#xD;&#xA;@heartade@blog.heartade.dev&#xD;&#xA;&#xD;&#xA;Follow my shorter shoutouts at Fediverse:&#xD;&#xA;@heartade@social.silicon.moe&#xD;&#xA;&#xD;&#xA;Follow me at Bluesky:&#xD;&#xA;@heartade.dev]]&gt;</description>
      <content:encoded><![CDATA[<p>이 글은 <a href="https://blog.heartade.dev/ripaegteoring-2pan-seuteodi-galmuri">리팩터링 2판 스터디</a> 시리즈 중 하나입니다.</p>

<p>리팩터링 2판 스터디의 네 번째 글입니다. 여러 명이 나눠서 정리하는 스터디였기 때문에 이 글을 마지막으로 스터디는 끝나는데, 나머지 내용도 제 블로그에 정리할지는 좀 고민해 봐야겠어요.</p>

<p>이 장은 말 그대로 코드를 옮겨야 하는 상황과 그 방법에 대해 다룹니다. 절차적 프로그래밍에서 코드를 절차상 적절한™ 위치로 옮기는 건 아주 중요하죠.
</p>

<h2 id="목차">목차</h2>
<ul><li><a href="#%EC%A0%95%EB%A6%AC">정리</a>
<ul><li><a href="#8-1-%ED%95%A8%EC%88%98-%EC%98%AE%EA%B8%B0%EA%B8%B0">8.1 함수 옮기기</a></li>
<li><a href="#8-2-%ED%95%84%EB%93%9C-%EC%98%AE%EA%B8%B0%EA%B8%B0">8.2 필드 옮기기</a></li>
<li><a href="#8-3-%EB%AC%B8%EC%9E%A5%EC%9D%84-%ED%95%A8%EC%88%98%EB%A1%9C-%EC%98%AE%EA%B8%B0%EA%B8%B0">8.3 문장을 함수로 옮기기</a></li>
<li><a href="#8-4-%EB%AC%B8%EC%9E%A5%EC%9D%84-%ED%98%B8%EC%B6%9C%ED%95%9C-%EA%B3%B3%EC%9C%BC%EB%A1%9C-%EC%98%AE%EA%B8%B0%EA%B8%B0">8.4 문장을 호출한 곳으로 옮기기</a></li>
<li><a href="#8-5-%EC%9D%B8%EB%9D%BC%EC%9D%B8-%EC%BD%94%EB%93%9C%EB%A5%BC-%ED%95%A8%EC%88%98-%ED%98%B8%EC%B6%9C%EB%A1%9C-%EB%B0%94%EA%BE%B8%EA%B8%B0">8.5 인라인 코드를 함수 호출로 바꾸기</a></li>
<li><a href="#8-6-%EB%AC%B8%EC%9E%A5-%EC%8A%AC%EB%9D%BC%EC%9D%B4%EB%93%9C%ED%95%98%EA%B8%B0">8.6 문장 슬라이드하기</a></li>
<li><a href="#8-7-%EB%B0%98%EB%B3%B5%EB%AC%B8-%EC%AA%BC%EA%B0%9C%EA%B8%B0">8.7 반복문 쪼개기</a></li>
<li><a href="#8-8-%EB%B0%98%EB%B3%B5%EB%AC%B8%EC%9D%84-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8%EC%9C%BC%EB%A1%9C-%EB%B0%94%EA%BE%B8%EA%B8%B0">8.8 반복문을 파이프라인으로 바꾸기</a></li>
<li><a href="#8-9-%EC%A3%BD%EC%9D%80-%EC%BD%94%EB%93%9C-%EC%A0%9C%EA%B1%B0%ED%95%98%EA%B8%B0">8.9 죽은 코드 제거하기</a></li></ul></li></ul>

<h2 id="정리">정리</h2>

<h3 id="8-1-함수-옮기기" id="8-1-함수-옮기기">8.1 함수 옮기기</h3>

<blockquote><p>모듈성을 높이려면 서로 연관된 요소들을 함께 묶고, 요소 사이의 연결 관계를 쉽게 찾고 이해할 수 있도록 해야 한다. 하지만 프로그램을 얼마나 잘 이해했느냐에 따라 구체적인 방법이 달라질 수 있다. 보통은 이해도가 높아질수록 소프트웨어 요소들을 더 잘 묶는 새로운 방법을 깨우치게 된다. 그래서 높아진 이해를 반영하려면 요소들을 이리저리 옮겨야 할 수 있다. <em>– p.278</em></p></blockquote>

<h4 id="절차-p-278" id="절차-p-278">절차 (p.278)</h4>
<ol><li>선택한 함수가 현재 컨택스트에서 사용 중인 모든 프로그램 요소를 살펴본다. 이 요소들 중에도 함께 옮겨야 할 게 있는지 고민해본다.</li>
<li>선택한 함수가 다형 메서드인지 확인한다.
<ul><li>객체 지향 언어의 다형성(polymorphism)을 구현하는 메서드인지 확인한다는 의미입니다. 슈퍼클래스나 서브클래스에도 선언된 메서드라면 전부 같이 옮겨 줘야 하니까요.</li></ul></li>
<li>선택한 함수를 타깃 컨텍스트로 복사한다(이 때 원래의 함수를 소스 함수라 하고 복사해서 만든 새로운 함수를 타깃 함수라 한다). 타깃 함수가 새로운 터전에 잘 자리잡도록 다듬는다.</li>
<li>정적 분석을 수행한다.</li>
<li>소스 컨텍스트에서 타깃 함수를 참조할 방법을 찾아 반영한다.</li>
<li>소스 함수를 타깃 함수의 위임 함수가 되도록 수정한다.</li>
<li>테스트한다.</li>
<li>소스 함수를 인라인(6.2절)할지 고민해본다.</li></ol>

<h4 id="요점">요점</h4>
<ul><li>여러 개의 함수를 함께 옮기는 경우, 호출하는 함수보다 호출되는 함수를 먼저 옮기는 게 낫습니다.
<ul><li>원문에 “하위 함수들의 호출자가 고수준 함수 하나뿐이면 먼저 하위 함수들을 고수준 함수에 인라인한 다음...“이라는 내용이 있는데, 여기서 말하는 고수준 함수가 <a href="https://en.wikipedia.org/wiki/Higher-order_function">Higher-Order Function</a>을 뜻하는 것인지 아니면 그냥 함수의 호출 관계를 하위/상위로 나눈 것인지 모르겠네요. 원서를 읽어 봐야 이해가 될 것 같습니다...</li></ul></li>
<li>여러 개의 함수를 함께 옮기는 경우, 코드의 다른 곳에 미치는 영향이 적은 함수를 먼저 옮깁시다.</li>
<li>옮겨지는 함수가 원래 있던 곳(소스 컨텍스트)의 요소들을 사용한다면 그 요소들을 매개변수로 넘기거나 컨텍스트 자체를 참조(레퍼런스)로 넘겨 줍시다.
<ul><li>컨텍스트 자체를 넘겨 준다는 것의 예를 들자면, <code>SomeClass.method()</code>를 <code>OtherClass</code>로 옮기고 싶은데 <code>method</code>의 구현에 <code>SomeClass</code>의 필드들을 참조해야 한다면 아예 <code>OtherClass.method(someClass)</code> 식으로 <code>SomeClass</code>의 인스턴스 참조를 넘길 수도 있다는 의미입니다.</li></ul></li>
<li>옮겨지는 함수를 호출하는 코드를 모두 한 번에 찾아서 바꾸는 게 아니라, 함수의 구현만 옮기고 기존 선언을 위임 함수(새로운 구현을 호출하는 함수)로 남겨두는 점이 특기할 만합니다. 이 위임 함수를 호출하는 코드를 직접 호출로 바꿀지는 마지막에 고민해도 괜찮으니 한 번에 최소한의 수정만 하자는 것이죠.</li></ul>

<h3 id="8-2-필드-옮기기" id="8-2-필드-옮기기">8.2 필드 옮기기</h3>

<blockquote><p>필드 옮기기 리팩터링은 대체로 더 큰 변경의 일환으로 수행된다. 예컨대 필드 하나를 잘 옮기면, 그 필드를 사용하던 많은 코드가 원래 위치보다 옮겨진 위치에서 사용하는 게 더 수월할 수 있다. 그렇다면 리팩터링을 마저 진행하여 호출 코드들까지 모두 변경한다. <em>– p.290</em></p></blockquote>

<h4 id="절차-p-290" id="절차-p-290">절차 (p.290)</h4>
<ol><li>소스 필드가 캡슐화되어 있지 않다면 캡슐화한다.</li>
<li>테스트한다.</li>
<li>타깃 객체에 필드(와 접근자 메서드들)를 생성한다.</li>
<li>정적 검사를 수행한다.</li>
<li>소스 객체에서 타깃 객체를 참조할 수 있는지 확인한다.
<ul><li>참조할 수 없으면 참조하게 만듭시다(...)</li></ul></li>
<li>접근자들이 타깃 필드를 사용하도록 수정한다.</li>
<li>테스트한다.</li>
<li>소스 필드를 제거한다.</li>
<li>테스트한다.</li></ol>

<h4 id="요점-1" id="요점-1">요점</h4>
<ul><li>크게 타깃 필드 만들기, 소스 컨텍스트의 접근자 수정하기, 소스 필드 없애기의 세 단계로 나눌 수 있겠습니다.
<ul><li>그런데 소스의 접근자는 그대로 둡니다. 이 시점에서 소스 컨텍스트의 접근자는 일종의 위임 함수가 된 상태인 셈인데, 위임 함수를 인라인할지 고민하는 건 별개의 문제라고 보는 것 같아요.</li></ul></li>
<li>여러 소스에서 같은 타깃을 공유한다면(즉 소스 객체가 타깃 객체를 다대일 관계로 참조한다면), 우선 소스 필드를 남겨둔 상태로 세터가 타깃 필드와 소스 필드 모두를 갱신하게 한 다음 어서션(10.6절)을 통해 일관성을 깨뜨리는 갱신을 검출해 봅시다.</li></ul>

<h3 id="8-3-문장을-함수로-옮기기" id="8-3-문장을-함수로-옮기기">8.3 문장을 함수로 옮기기</h3>

<blockquote><p>중복 제거는 코드를 건강하게 관리하는 가장 효과적인 방법 중 하나다. 예컨대 특정 함수를 호출하는 코드가 나올 때마다 그 앞이나 뒤에서 똑같은 코드가 추가로 실행되는 모습을 보면, 나는 그 반복되는 부분을 피호출 함수로 합치는 방법을 궁리한다. <em>– p.296</em></p></blockquote>

<h4 id="절차-p-297" id="절차-p-297">절차 (p.297)</h4>
<ol><li>반복 코드가 함수 호출 부분과 멀리 떨어져 있다면 문장 슬라이드하기(8.6절)를 적용해 근처로 옮긴다.</li>
<li>타깃 함수를 호출하는 곳이 한 곳뿐이면, 단순히 소스 위치에서 해당 코드를 잘라내어 피호출 함수로 복사하고 테스트한다. 이 경우라면 나머지 단계는 무시한다.</li>
<li>호출자가 둘 이상이면 호출자 중 하나에서 &#39;타깃 함수 호출 부분과 그 함수로 옮기려는 문장들을 함께&#39; 다른 함수로 추출(6.1절)한다. 추출한 함수에 기억하기 쉬운 임시 이름을 지어준다.</li>
<li>다른 호출자 모두가 방금 추출한 함수를 사용하도록 수정한다. 하나씩 수정할 때마다 테스트한다.</li>
<li>모든 호출자가 새로운 함수를 사용하게 되면 원래 함수를 새로운 함수 안으로 인라인(6.2절)한 후 원래 함수를 제거한다.</li>
<li>새로운 함수의 이름을 원래 함수의 이름으로 바꿔 준다(함수 이름 바꾸기, 6.5절).
<ul><li>더 나은 이름이 있다면 그 이름을 쓴다.</li></ul></li></ol>

<h4 id="요점-2" id="요점-2">요점</h4>
<ul><li>단순히 원래 있던 함수 안으로 코드를 옮기는 게 아니라, 원래 함수와 함수 안으로 옮길 코드를 함께 호출하는 새로운 함수를 만들고, 각각의 호출을 이 새 함수로 바꿔 가며 테스트한 다음, 그제서야 문제가 없으면 원래 함수의 내용을 새 함수 안으로 옮깁니다. 이렇게 하면 반복 코드의 미묘한 차이로 문제가 생기는 상황을 잡아낼 수 있습니다.</li>
<li>완전히 하나의 함수로 합칠 수 있는 상황은 아니지만 여전히 해당 함수와 함께 사용되어야만 하는 코드라면 (예를 들어 어떤 코드가 어느 함수를 호출할 때만 사용되지만, 그 함수를 호출할 때마다 그 코드를 사용하는 것은 아니라면) 함수 안으로 옮기는 대신 위 절차의 4번까지만 수행해서 새로운 함수로 만듭니다.</li></ul>

<h3 id="8-4-문장을-호출한-곳으로-옮기기" id="8-4-문장을-호출한-곳으로-옮기기">8.4 문장을 호출한 곳으로 옮기기</h3>

<p>앞 절의 반대입니다. 앞 절에서는 어떤 함수를 호출할 때 함수 밖에서 항상 동일한 동작을 수행한다면 그 동작을 함수 안으로 옮기는 방식이었는데, 반대로 어떤 함수를 호출할 때 그 함수 안의 한 부분이 경우에 따라 다르게 동작해야 한다면 그 부분을 함수 밖으로 꺼냅니다.</p>

<h4 id="절차-p-302" id="절차-p-302">절차 (p.302)</h4>
<ol><li>호출자가 한두 개뿐이고 피호출 함수도 간단한 단순한 상황이면, 피호출 함수의 처음(혹은 마지막) 줄(들)을 잘라내어 호출자(들)로 복사해 넣는다(필요하면 적당히 수정한다). 테스트만 통과하면 이번 리팩터링은 여기서 끝이다.</li>
<li>더 복잡한 상황에서는, 이동하지 &#39;않길&#39; 원하는 모든 문장을 함수로 추출(6.1절)한 다음 검색하기 쉬운 임시 이름을 지어준다.</li>
<li>원래 함수를 인라인(6.2절)한다.</li>
<li>추출된 함수의 이름을 원래 함수의 이름으로 변경한다(함수 이름 바꾸기, 6.5절).
<ul><li>더 나은 이름이 있다면 그 이름을 쓴다.</li></ul></li></ol>

<h4 id="요점-3" id="요점-3">요점</h4>
<ul><li>&#39;이동하지 않길 원하는 문장을 추출&#39;하는 상황에서, 대상 함수가 다형 메서드라면 모든 구현에서 남길 부분을 메서드로 추출한 다음 (이 부분은 당연히 모든 구현에서 동일해야 합니다) 슈퍼클래스의 메서드만 남깁니다. 이 때 남길 부분은 모든 구현에서 동일한 부분이어야 합니다.</li></ul>

<h3 id="8-5-인라인-코드를-함수-호출로-바꾸기" id="8-5-인라인-코드를-함수-호출로-바꾸기">8.5 인라인 코드를 함수 호출로 바꾸기</h3>

<blockquote><p>이미 존재하는 함수와 똑같은 일을 하는 인라인 코드를 발견하면 보통은 해당 코드를 함수 호출로 대체하길 원할 것이다. 예외가 있다면 [...] 기존 함수의 코드를 수정하더라도 인라인 코드의 동작은 바뀌지 않아야 할 때뿐이다. 이 경우인가를 판단하는 데는 함수 이름이 힌트가 된다. 이름을 잘 지었다면 인라인 코드 대신 함수 이름을 넣어도 말이 된다. <em>– p.308</em></p></blockquote>

<h4 id="절차-p-309" id="절차-p-309">절차 (p.309)</h4>
<ol><li>인라인 코드를 함수 호출로 대체한다.</li>
<li>테스트한다.</li></ol>

<h4 id="요점-4" id="요점-4">요점</h4>
<ul><li>이 절은 절차가 수상하게 단순한데, 인라인 코드가 이미 다른 함수에 구현된 일을 한다는 것이 명확한 상황에서 그 코드를 함수로 대체하는 일에 대한 내용이기 때문입니다. 그냥 바퀴를 재발명하지 말자는 내용입니다.</li>
<li>자주 사용되는 코드가 있고, 그 코드를 라이브러리 함수나 기존에 구현된 다른 함수로 대체할 수 없다면 함수 추출하기(6.1절)를 사용해서 함수로 만들어 줍시다.</li></ul>

<h3 id="8-6-문장-슬라이드하기" id="8-6-문장-슬라이드하기">8.6 문장 슬라이드하기</h3>

<blockquote><p>관련 코드끼리 모으는 작업은 다른 리팩터링(주로 함수 추출하기; 6.1절)의 준비 단계로 자주 행해진다. 관련 있는 코드들을 명확히 구분되는 함수로 추출하는 게 그저 문장들을 한데로 모으는 것보다 나은 분리법이다. 하지만 코드들이 모여 있지 않다면 함수 추출은 애초에 수행할 수조차 없다. <em>– p.310</em></p></blockquote>

<h4 id="절차-p-311" id="절차-p-311">절차 (p.311)</h4>
<ol><li>코드 조각(문장들)을 이동할 목표 위치를 찾는다. 코드 조각의 원래 위치와 목표 위치 사이의 코드들을 훑어보면서, 조각을 모으고 나면 동작이 달라지는 코드가 있는지 살핀다.
<ul><li>다음과 같은 간섭이 있다면 포기한다.
<ul><li>코드 조각에서 참조하는 요소를 선언하는 문장 앞으로는 이동할 수 없다.</li>
<li>코드 조각을 참조하는 요소의 뒤로는 이동할 수 없다.</li>
<li>코드 조각에서 참조하는 요소를 수정하는 문장을 건너뛰어 이동할 수 없다.</li>
<li>코드 조각이 수정하는 요소를 참조하는 요소를 건너뛰어 이동할 수 없다.</li></ul></li></ul></li>
<li>코드 조각을 원래 위치에서 잘라내어 목표 위치에 붙여넣는다.</li>
<li>테스트한다.</li></ol>

<h4 id="요점-5" id="요점-5">요점</h4>
<ul><li>이 책에서 가장 자주 언급되는 리팩터링 기법 중 하나인 것 같은데, 말 그대로 코드 블록을 위아래로 옮기는 것입니다. VS Code에서 <code>Alt+↑</code> 아니면 <code>Alt+↓</code>로 하는 그거죠.</li>
<li>절차에서 &#39;다음과 같은 간섭&#39;을 언급하는데, 간단히 요약하자면 코드의 인과 관계를 유지하는 선에서 옮겨야 한다는 것입니다.</li>
<li>한 문장만 옮길 때는 &#39;<a href="https://www.industriallogic.com/blog/swap-statement-refactoring/">문장 교환하기(Swap Statement)</a>&#39;라고 부른다고 하네요.</li></ul>

<h3 id="8-7-반복문-쪼개기" id="8-7-반복문-쪼개기">8.7 반복문 쪼개기</h3>

<blockquote><p>종종 반복문 하나에서 두 가지 일을 수행하는 모습을 보게 된다. 그저 두 일을 한꺼번에 처리할 수 있다는 이유에서 말이다. 하지만 이렇게 하면 반복문을 수정해야 할 때마다 두 가지 일 모두를 잘 이해하고 진행해야 한다. 반대로 각각의 반복문으로 분리해 두면 수정할 동작 하나만 이해하면 된다. <em>– p.316</em></p></blockquote>

<h4 id="절차-p-317" id="절차-p-317">절차 (p.317)</h4>
<ol><li>반복문을 복제해 두 개로 만든다.</li>
<li>반복문이 중복되어 생기는 부수효과를 파악해서 제거한다.</li>
<li>테스트한다.</li>
<li>완료됐으면, 각 반복문을 함수로 추출(6.1절)할지 고민해본다.</li></ol>

<h4 id="요점-6" id="요점-6">요점</h4>
<ul><li>이 리팩터링은 각 반복문을 다른 함수로 추출해야 할지 고민해 보는 것으로 마무리됩니다. 하나의 반복문 안에서 여러 일이 일어나고 있어서 쪼개야 한다면, 그 함수가 여러 기능을 수행하고 있다는 신호일 수 있기 때문입니다.</li>
<li>본문에서는 한 문단을 할애하여 최적화를 할 거면 리팩터링을 먼저 하고 하라고 호소하고 있습니다. &#39;반복문 하나에서 처리할 수 있는 일을 왜 굳이 쪼개지?!&#39; 라는 생각이 들 때가 있긴 하지만, 미래의 우리는 그 반복문의 역할을 까먹을 것이므로 유지보수를 위해 역할별로 쪼개어 주는 것이 아무래도 좋겠습니다.</li></ul>

<h3 id="8-8-반복문을-파이프라인으로-바꾸기" id="8-8-반복문을-파이프라인으로-바꾸기">8.8 반복문을 파이프라인으로 바꾸기</h3>

<blockquote><p>논리를 파이프라인으로 표현하면 이해하기 훨씬 쉬워진다. 객체가 파이프라인을 따라 흐르며 어떻게 처리되는지를 읽을 수 있기 때문이다. <em>– p.320</em></p></blockquote>

<h4 id="절차-p-320" id="절차-p-320">절차 (p.320)</h4>
<ol><li>반복문에서 사용하는 컬렉션을 가리키는 변수를 하나 만든다.</li>
<li>반복문의 첫 줄부터 시작해서, 각각의 단위 행위를 적절한 컬렉션 파이프라인 연산으로 대체한다. 이때 컬렉션 파이프라인 연산은 1. 에서 만든 반복문 컬렉션 변수에서 시작하여, 이전 연산의 결과를 기초로 연쇄적으로 수행된다. 하나를 대체할 때마다 테스트한다.</li>
<li>반복문의 모든 동작을 대체했다면 반복문 자체를 지운다.</li></ol>

<h4 id="요점-7" id="요점-7">요점</h4>
<ul><li><code>filter</code>, <code>map</code>, <code>reduce</code> 같은 컬렉션 파이프라인을 쓰자는 내용입니다. 아직 안 쓰고 계셨다면 쓰세요. 두 번 쓰세요.</li></ul>

<h3 id="8-9-죽은-코드-제거하기" id="8-9-죽은-코드-제거하기">8.9 죽은 코드 제거하기</h3>

<blockquote><p>코드가 더 이상 사용되지 않게 됐다면 지워야 한다. 혹시 다시 필요해질 날이 오지 않을까 걱정할 필요 없다. 우리에겐 버전 관리 시스템이 있다! <em>– p.327</em></p></blockquote>

<h4 id="절차-p-328" id="절차-p-328">절차 (p.328)</h4>
<ol><li>죽은 코드를 외부에서 참조할 수 있는 경우라면(예컨대 함수 하나가 통째로 죽었을 때) 혹시라도 호출하는 곳이 있는지 확인한다.</li>
<li>없다면 죽은 코드를 제거한다.</li>
<li>테스트한다.</li></ol>

<h4 id="요점-8" id="요점-8">요점</h4>
<ul><li>안 쓰는 코드를 주석 처리하지 말고 지우라고 해서 좀 찔립니다. 깃을 열심히 써야겠어요.</li></ul>

<hr>
<ul><li>태그: <a href="https://blog.heartade.dev/tag:%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81" class="hashtag"><span>#</span><span class="p-category">리팩터링</span></a> <a href="https://blog.heartade.dev/tag:%EA%B3%B5%EB%B6%80" class="hashtag"><span>#</span><span class="p-category">공부</span></a> <a href="https://blog.heartade.dev/tag:%EC%8A%A4%ED%84%B0%EB%94%94" class="hashtag"><span>#</span><span class="p-category">스터디</span></a></li></ul>



<hr>

<h3 id="dani-soohan-park-heartade" id="dani-soohan-park-heartade">Dani Soohan Park (@heartade)</h3>

<p>Follow this blog at Fediverse:
<a href="https://blog.heartade.dev/heartade"><a href="https://blog.heartade.dev/@/heartade@blog.heartade.dev" class="u-url mention">@<span>heartade@blog.heartade.dev</span></a></a></p>

<p>Follow my shorter shoutouts at Fediverse:
<a href="https://social.silicon.moe/@heartade"><a href="https://blog.heartade.dev/@/heartade@social.silicon.moe" class="u-url mention">@<span>heartade@social.silicon.moe</span></a></a></p>

<p>Follow me at Bluesky:
<a href="https://bsky.app/profile/did:plc:e4a32z23pazq5dxnucj6wpee">@heartade.dev</a></p>
]]></content:encoded>
      <guid>https://blog.heartade.dev/ripaegteoring-2pan-seuteodi-8jang-gineung-idong</guid>
      <pubDate>Thu, 09 Feb 2023 03:00:00 +0000</pubDate>
    </item>
    <item>
      <title>리팩터링 2판 스터디: 3장 &#39;코드에서 나는 악취&#39;</title>
      <link>https://blog.heartade.dev/ripaegteoring-2pan-seuteodi-3jang-kodeueseo-naneun-agcwi</link>
      <description>&lt;![CDATA[이 글은 리팩터링 2판 스터디 시리즈 중 하나입니다.&#xA;&#xA;리팩터링 2판 스터디의 세 번째 글입니다. 오늘은 냄새가 나는... 그러니까 리팩터링해야 하는 코드가 어떤 것인지 구분하는 방법을 알아봅니다.&#xA;&#xA;이 책은 대체로 마틴 파울러가 썼지만, 이 챕터는 마틴 파울러의 멘토 겸 지인인 켄트 벡(Kent Beck)이 함께 집필했습니다(켄트 벡은 애자일, 테스트 주도 개발 등 여러 개발 방법론의 선구자이기도 합니다). 원서는 마틴 파울러, 켄트 벡 공저로 되어 있고 한국어판은 마틴 파울러 집필로 되어 있는 게 의아했는데, 이 장에만 참여해서 한국어판의 저자 목록에서는 빠진 모양이에요.&#xA;&#xA;  이 장은 켄트와 내가 함께 집필했다는 점을 강조하기 위해 &#39;나&#39;가 아닌 &#39;우리&#39;란 표현을 사용한다. 어느 부분을 누가 쓴 것인지는 쉽게 구분할 수 있다. 웃긴 농담은 필자가 쓴 것이고 나머지는 켄트가 쓴 것이다. - p.113&#xA;&#xA;!--more--&#xA;목차 같은 건 생략하고 간단히 요점만 정리해 봅시다. 원래 각각의 증상에 맞는 대처법을 함께 요약해 두려고 했는데, 앞으로 한참 더 이야기할 내용이기도 하고 분량도 너무 많아져서 과감히 생략하기로 했어요(책의 내용을 충분히 정리하고 나면 나중에 다시 돌아와서 넣을지도 모르죠).&#xA;&#xA;리팩터링해야 할 코드의 징후&#xA;&#xA;3.1 기이한 이름&#xA;&#xA;  하지만 아쉽게도 이름 짓기는 프로그래밍에서 가장 어렵기로 손꼽히는 두 가지 중 하나다. - p.114&#xA;&#xA; 함수, 모듈, 변수, 클래스의 이름만 봐도 역할과 사용법을 이해할 수 있는 이름을 지어야 합니다.&#xA; 이름을 짓기가 너무 어렵다면 설계 자체에 문제가 있다는 신호, 즉 해당 함수나 변수 등등의 역할 자체가 모호하다는 신호일 수 있습니다.&#xA;&#xA;3.2 중복 코드&#xA;&#xA; 같은 코드가 여러 곳에 있으면 각각을 수정할 때마다 비슷한 다른 코드들도 수정해야 하는 부담이 생깁니다.&#xA;&#xA;3.3 긴 함수&#xA;&#xA;  간접 호출(indirection)의 효과, 즉 코드를 이해하고, 공유하고, 선택하기 쉬워진다는 장점은 함수를 짧게 구성할 때 나오는 것이다. - p.115&#xA;&#xA; 함수가 길수록 이해하기 어렵습니다. &#xA; 요즘 언어는 프로세스 안에서의 함수 호출 비용을 거의 없애 버렸다고 하네요. (어떤 언어 얘기인지, 얼마나 없어진 건지 확인해 보기엔 제 로우레벨 지식이 한미하네요. 나중에 좀 더 자세히 공부해 봐야겠어요.)&#xA; 짧은 함수가 많아지면 읽는 사람 입장에서는 함수 선언과 호출을 왔다갔다하는 것이 부담이 되지만, 함수 이름을 잘 지으면 함수의 구현을 보지 않아도 쉽게 쓸 수 있게 됩니다.&#xA;&#xA;3.4 긴 매개변수 목록&#xA;&#xA; 전역 변수를 사용하는 것보다는 매개변수를 사용하는 것이 훨씬 좋지만, 매개변수가 너무 많아도 코드를 이해하기 어려우니 가능하다면 매개변수의 수를 줄여 봅시다. 다른 매개변수로부터 계산해낼 수 있는 매개변수를 질의 함수로 바꾸거나(11.5절), 객체 자체를 넘길 수도 있습니다(11.4절).&#xA;   사족이지만, 이래서 저는 ({x, y, r}: Circle) =  {/.../} 식의 구조분해 할당(Destructuring Assignment) 문법을 좋아합니다. 일단 객체를 통째로 넘긴 다음 필요하면 꺼내 쓰고, 아니면 말고 식으로 사용하기가 훨씬 편해지거든요.&#xA;&#xA;3.5 전역 데이터&#xA;&#xA;  전역 데이터를 주의해야 한다는 말은 우리가 소프트웨어 개발을 시작한 초창기부터 귀가 따갑게 들었다. 심지어 전역 데이터는 이를 함부로 사용한 프로그래머들에게 벌을 주는 지옥 4층에 사는 악마들이 만들었다는 말이 돌 정도였다. - p.117&#xA;&#xA; 전역 데이터를 방지하기 위해 대표적으로 변수 캡슐화(6.6절)를 사용합니다.&#xA;&#xA;3.6 가변 데이터&#xA;&#xA; 책에서 마르고 닳도록 하는 이야기인데, 가변 데이터는 예기치 못한 부작용(side effect)을 불러옵니다. 최대한 데이터를 불변으로 유지하고, 데이터를 갱신하는 코드를 최대한 한 곳에 모아서 관리합시다.&#xA;&#xA;3.7 뒤엉킨 변경&#xA;&#xA;  예컨대 지원해야 할 데이터베이스가 추가될 때마다 함수 세 개를 바꿔야 하고, 금융 상품이 추가될 때마다 또 다른 함수 네 개를 바꿔야 하는 모듈이 있다면 뒤엉킨 변경이 발생했다는 뜻이다. - p.120&#xA;&#xA; 하나의 모듈이 변경되는 이유는 오직 하나여야 합니다(단일 책임 원칙; Single Responsibility Principle). 달리 말하자면 그 코드를 수정할 때는 항상 동일한 기능적 맥락 하에서 수정하는 것이어야 합니다. 그렇지 않다면 한 가지 기능을 수정할 때 코드가 수행하는 다른 기능에 영향을 미치면서 예기치 못한 오류가 발생하기 쉽습니다.&#xA; 위의 원칙을 구현하기 위해 코드를 기능적 맥락에 따라 나눠 봅시다.&#xA;&#xA;3.8 산탄총 수술&#xA;&#xA; 앞 절(하나의 코드가 여러 종류의 변경 사항에 의해 변경되는 경우)과 반대로 하나의 변경 사항을 구현하기 위해 여러 코드를 수정해야 하는 경우입니다. 이것도 마찬가지로 코드의 구조와 기능적 맥락이 제대로 대응하지 않아서 발생하는 문제인데, 함께 변경되는 코드들을 한 모듈로 묶거나, 아예 분리된 로직을 하나의 함수나 클래스로 합친 다음 기능적 맥락에 따라 다시 깔끔하게 분리하는 것도 좋습니다.&#xA;&#xA;3.9 기능 편애&#xA;&#xA; 프로그램을 모듈화할 때는 대부분의 상호작용이 모듈 내에서 이루어지고, 모듈들 사이의 상호작용은 최소한이 되어야 합니다. 만약 어떤 로직이 자신의 모듈보다 다른 모듈과 더 많이 상호작용한다면, 그 모듈로 옮겨 줍시다.&#xA;   일부 디자인 패턴에서는 예외가 발생합니다. 예를 들어, 전략 패턴(Strategy pattern)에서는 프로그램이 실행 시간에 여러 알고리즘 중 하나를 선택할 수 있게 하는데, 알고리즘을 호출하는 부분과 알고리즘이 구현된 부분을 합쳐 버리면 빛이 바랩니다.&#xA;&#xA;3.10 데이터 뭉치&#xA;&#xA; 데이터 여러 개가 항상 함께 몰려다닌다면 구조체로 모아 줍시다. 항상 특정한 조합으로 모여 있어야 하는 데이터를 찾는 방법 중 하나는 데이터 중 하나를 없애더라도 나머지 데이터에 의미가 있는지 찾아보는 것입니다. 해당 데이터들을 연계하는 로직이나 파생되는 값 등등을 함께 클래스로 모아 주면 더욱 효과적입니다.&#xA;&#xA;3.11 기본형 집착&#xA;&#xA; 화폐, 좌표, 구간, 단위를 가진 물리량 등을 적극적으로 객체로 표현합시다. 그러니까, getOilPrice(unitPrice: number, distance: number): number보다 getOilPrice(unitPrice: Currency, distance: Length): Currency 식으로 표현하면 미터인지 마일인지 같은 정보를 함께 넘겨줄 수도 있고 toString() 함수에 화폐 단위를 붙여 줄 수도 있다는 것입니다. 전화번호도 &#34;+82 010-0000-0000&#34; 같은 문자열로 저장하는 것보다 {countryCode: 82, digits: [&#39;010&#39;, &#39;0000&#39;, &#39;0000&#39;]} 식으로 저장하면 입력 정제와 출력 형식 지정이 보다 간편해집니다.&#xA;&#xA;3.12 반복되는 switch문&#xA;&#xA;  순수한 객체 지향을 신봉하는 사람들과 얘기하다 보면 주제는 곧 switch문의 사악함으로 흘러가기 마련이다. - p.123&#xA;&#xA; 똑같은 조건부 로직이 반복해서 등장한다면 클래스 다형성을 사용하는 방식으로 바꿔 봅시다.&#xA;&#xA;3.13 반복문&#xA;&#xA;  반복문은 프로그래밍 언어가 등장할 때부터 함께 한 핵심 프로그래밍 요소다. 하지만 이제는 1970년대에 유행하던 나팔바지나 솜털 무늬 벽지보다도 못한 존재가 됐다. - p.124&#xA;&#xA; 사실 이제는 다들 Off-By-One Error(찾아 봐도 마땅한 한국어 역어가 나오지 않네요)에 하도 데여서 이미 반복문 사용을 자제하고 있을 거예요. map(), reduce(), filter() 같은 파이프라인 연산을 사용합시다.&#xA;&#xA;3.16 임시 필드&#xA;&#xA; 특정 상황에서만 값이 설정되는 필드가 있다면 코드를 이해하기 어렵고, 예측하지 못한 undefined와 마주하게 됩니다. 이들을 사용되는 상황에 따라 모아서 새 클래스로 분리해 주면 코드가 보다 쉬워집니다.&#xA;&#xA;3.17 메시지 체인&#xA;&#xA; someInstance.getSomething().getOther().getAnother() 식으로 객체 요청이 연쇄적으로 이루어진다면 자주 요청되는 객체가 무엇인지 찾아보고 someInstance.getAnother() 식으로 요청할 수 있게 만들거나 체인을 적절히 다른 함수로 옮겨 코드 가독성을 높여 봅시다.&#xA;&#xA;3.18 중개자&#xA;&#xA; 다른 클래스의 메서드를 호출하는 일을 위주로 돌아가는 클래스나 함수가 있다면 수정의 중간 단계가 적어지도록 해당 클래스나 함수를 사용하는 클라이언트 코드에서 직접 대상을 호출하도록 변경합시다.&#xA;&#xA;3.19 내부자 거래&#xA;&#xA; 서로 다른 모듈 사이의 상호작용을 최대한 줄이고, 한 가지 일을 하기 위해 여러 모듈이 상호작용하는 상황이라면 그 일을 처리하는 모듈을 새로 만들거나 중재자 역할을 할 모듈을 새로 만듭시다. 클래스의 상속 구조에서 모듈 간의 의존성이 너무 높아진다면 상속 구조를 위임(delegate) 구조로 바꿔 서브클래스가 새로 정의하는 값만 담당하는 클래스를 따로 만드는 방법도 있습니다.&#xA;&#xA;3.20 거대한 클래스&#xA;&#xA; 클래스가 너무 커서 이해하기 어렵다면 우선 중복 코드를 추출해 보고, 그래도 너무 길다면 여러 클래스로 쪼개 봅시다.&#xA;&#xA;3.21 서로 다른 인터페이스의 대안 클래스들&#xA;&#xA; 유사한 맥락에서 서로 교체해 가며 사용할 수 있는 클래스들이 서로 다른 인터페이스를 구현한다면 문제가 생깁니다. 이 클래스들이 같은 인터페이스들 구현하도록 두 인터페이스 사이의 차이를 개별 클래스에서 구현하도록 바꾼 다음 다시 공통된 부분을 인터페이스나 슈퍼클래스로 올려 봅시다.&#xA;&#xA;3.22 데이터 클래스&#xA;&#xA; 가변 데이터 클래스를 사용하는 클래스에서 부작용이 일어나지 않도록 public 필드 사용을 자제하고, 변경하면 안 되는 값들의 세터를 제거합시다. 한편 데이터 클래스를 사용하는 동작이 다른 곳에 정의되어 있다면 동작 코드를 클래스 안으로 가져올 수도 있습니다.&#xA;&#xA;3.23 상속 포기&#xA;&#xA; 서브클래스가 부모 클래스의 구현을 따르지 않는 경우 상속 구조가 잘못되어 있다는 의미일 가능성이 높습니다. 서브클래스에서 사용하지 않는 구현은 별도의 서브클래스로 옮겨 부모 클래스에는 공통된 부분만 남도록 합시다. 한편 서브클래스가 부모 클래스의 인터페이스를 따르지 않는 경우 상속 자체가 어울리지 않는 경우일 수 있으니 위임(delegate) 클래스로 바꿔 봅시다.&#xA;&#xA;3.24 주석&#xA;&#xA;  주석을 남겨야겠다는 생각이 들면, 가장 먼저 주석이 필요 없는 코드로 리팩터링해본다. - p.131&#xA;&#xA; 동작에 대한 설명, 진행 상황, //TODO, //FIXME 등의 주석은 도움이 되지만, 주석이 너무 장황해진다면 코드 자체가 문제일 가능성이 높습니다.&#xA;&#xA;---&#xA; 태그: #리팩터링 #공부 #스터디&#xA;&#xA;!--more--&#xD;&#xA;---&#xD;&#xA;Dani Soohan Park (@heartade)&#xD;&#xA;&#xD;&#xA;Follow this blog at Fediverse:&#xD;&#xA;@heartade@blog.heartade.dev&#xD;&#xA;&#xD;&#xA;Follow my shorter shoutouts at Fediverse:&#xD;&#xA;@heartade@social.silicon.moe&#xD;&#xA;&#xD;&#xA;Follow me at Bluesky:&#xD;&#xA;@heartade.dev]]&gt;</description>
      <content:encoded><![CDATA[<p>이 글은 <a href="https://blog.heartade.dev/ripaegteoring-2pan-seuteodi-galmuri">리팩터링 2판 스터디</a> 시리즈 중 하나입니다.</p>

<p>리팩터링 2판 스터디의 세 번째 글입니다. 오늘은 냄새가 나는... 그러니까 리팩터링해야 하는 코드가 어떤 것인지 구분하는 방법을 알아봅니다.</p>

<p>이 책은 대체로 마틴 파울러가 썼지만, 이 챕터는 마틴 파울러의 멘토 겸 지인인 켄트 벡(Kent Beck)이 함께 집필했습니다(켄트 벡은 애자일, 테스트 주도 개발 등 여러 개발 방법론의 선구자이기도 합니다). 원서는 마틴 파울러, 켄트 벡 공저로 되어 있고 한국어판은 마틴 파울러 집필로 되어 있는 게 의아했는데, 이 장에만 참여해서 한국어판의 저자 목록에서는 빠진 모양이에요.</p>

<blockquote><p>이 장은 켄트와 내가 함께 집필했다는 점을 강조하기 위해 &#39;나&#39;가 아닌 &#39;우리&#39;란 표현을 사용한다. 어느 부분을 누가 쓴 것인지는 쉽게 구분할 수 있다. 웃긴 농담은 필자가 쓴 것이고 나머지는 켄트가 쓴 것이다. <em>– p.113</em></p></blockquote>



<p>목차 같은 건 생략하고 간단히 요점만 정리해 봅시다. 원래 각각의 증상에 맞는 대처법을 함께 요약해 두려고 했는데, 앞으로 한참 더 이야기할 내용이기도 하고 분량도 너무 많아져서 과감히 생략하기로 했어요(책의 내용을 충분히 정리하고 나면 나중에 다시 돌아와서 넣을지도 모르죠).</p>

<h2 id="리팩터링해야-할-코드의-징후" id="리팩터링해야-할-코드의-징후">리팩터링해야 할 코드의 징후</h2>

<h3 id="3-1-기이한-이름" id="3-1-기이한-이름">3.1 기이한 이름</h3>

<blockquote><p><a href="https://martinfowler.com/bliki/TwoHardThings.html">하지만 아쉽게도 이름 짓기는 프로그래밍에서 가장 어렵기로 손꼽히는 두 가지 중 하나다.</a> <em>– p.114</em></p></blockquote>
<ul><li>함수, 모듈, 변수, 클래스의 이름만 봐도 역할과 사용법을 이해할 수 있는 이름을 지어야 합니다.</li>
<li>이름을 짓기가 너무 어렵다면 설계 자체에 문제가 있다는 신호, 즉 해당 함수나 변수 등등의 역할 자체가 모호하다는 신호일 수 있습니다.</li></ul>

<h3 id="3-2-중복-코드" id="3-2-중복-코드">3.2 중복 코드</h3>
<ul><li>같은 코드가 여러 곳에 있으면 각각을 수정할 때마다 비슷한 다른 코드들도 수정해야 하는 부담이 생깁니다.</li></ul>

<h3 id="3-3-긴-함수" id="3-3-긴-함수">3.3 긴 함수</h3>

<blockquote><p>간접 호출(indirection)의 효과, 즉 코드를 이해하고, 공유하고, 선택하기 쉬워진다는 장점은 함수를 짧게 구성할 때 나오는 것이다. <em>– p.115</em></p></blockquote>
<ul><li>함수가 길수록 이해하기 어렵습니다.</li>
<li>요즘 언어는 프로세스 안에서의 함수 호출 비용을 거의 없애 버렸다고 하네요. (어떤 언어 얘기인지, 얼마나 없어진 건지 확인해 보기엔 제 로우레벨 지식이 한미하네요. 나중에 좀 더 자세히 공부해 봐야겠어요.)</li>
<li>짧은 함수가 많아지면 읽는 사람 입장에서는 함수 선언과 호출을 왔다갔다하는 것이 부담이 되지만, 함수 이름을 잘 지으면 함수의 구현을 보지 않아도 쉽게 쓸 수 있게 됩니다.</li></ul>

<h3 id="3-4-긴-매개변수-목록" id="3-4-긴-매개변수-목록">3.4 긴 매개변수 목록</h3>
<ul><li>전역 변수를 사용하는 것보다는 매개변수를 사용하는 것이 훨씬 좋지만, 매개변수가 너무 많아도 코드를 이해하기 어려우니 가능하다면 매개변수의 수를 줄여 봅시다. 다른 매개변수로부터 계산해낼 수 있는 매개변수를 질의 함수로 바꾸거나(11.5절), 객체 자체를 넘길 수도 있습니다(11.4절).
<ul><li>사족이지만, 이래서 저는 <code>({x, y, r}: Circle) =&gt; {/*...*/}</code> 식의 구조분해 할당(<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment">Destructuring Assignment</a>) 문법을 좋아합니다. 일단 객체를 통째로 넘긴 다음 필요하면 꺼내 쓰고, 아니면 말고 식으로 사용하기가 훨씬 편해지거든요.</li></ul></li></ul>

<h3 id="3-5-전역-데이터" id="3-5-전역-데이터">3.5 전역 데이터</h3>

<blockquote><p>전역 데이터를 주의해야 한다는 말은 우리가 소프트웨어 개발을 시작한 초창기부터 귀가 따갑게 들었다. 심지어 전역 데이터는 이를 함부로 사용한 프로그래머들에게 벌을 주는 지옥 4층에 사는 악마들이 만들었다는 말이 돌 정도였다. <em>– p.117</em></p></blockquote>
<ul><li>전역 데이터를 방지하기 위해 대표적으로 변수 캡슐화(6.6절)를 사용합니다.</li></ul>

<h3 id="3-6-가변-데이터" id="3-6-가변-데이터">3.6 가변 데이터</h3>
<ul><li>책에서 마르고 닳도록 하는 이야기인데, 가변 데이터는 예기치 못한 부작용(side effect)을 불러옵니다. 최대한 데이터를 불변으로 유지하고, 데이터를 갱신하는 코드를 최대한 한 곳에 모아서 관리합시다.</li></ul>

<h3 id="3-7-뒤엉킨-변경" id="3-7-뒤엉킨-변경">3.7 뒤엉킨 변경</h3>

<blockquote><p>예컨대 지원해야 할 데이터베이스가 추가될 때마다 함수 세 개를 바꿔야 하고, 금융 상품이 추가될 때마다 또 다른 함수 네 개를 바꿔야 하는 모듈이 있다면 뒤엉킨 변경이 발생했다는 뜻이다. <em>– p.120</em></p></blockquote>
<ul><li>하나의 모듈이 변경되는 이유는 오직 하나여야 합니다(단일 책임 원칙; Single Responsibility Principle). 달리 말하자면 그 코드를 수정할 때는 항상 동일한 기능적 맥락 하에서 수정하는 것이어야 합니다. 그렇지 않다면 한 가지 기능을 수정할 때 코드가 수행하는 다른 기능에 영향을 미치면서 예기치 못한 오류가 발생하기 쉽습니다.</li>
<li>위의 원칙을 구현하기 위해 코드를 기능적 맥락에 따라 나눠 봅시다.</li></ul>

<h3 id="3-8-산탄총-수술" id="3-8-산탄총-수술">3.8 산탄총 수술</h3>
<ul><li>앞 절(하나의 코드가 여러 종류의 변경 사항에 의해 변경되는 경우)과 반대로 하나의 변경 사항을 구현하기 위해 여러 코드를 수정해야 하는 경우입니다. 이것도 마찬가지로 코드의 구조와 기능적 맥락이 제대로 대응하지 않아서 발생하는 문제인데, 함께 변경되는 코드들을 한 모듈로 묶거나, 아예 분리된 로직을 하나의 함수나 클래스로 합친 다음 기능적 맥락에 따라 다시 깔끔하게 분리하는 것도 좋습니다.</li></ul>

<h3 id="3-9-기능-편애" id="3-9-기능-편애">3.9 기능 편애</h3>
<ul><li>프로그램을 모듈화할 때는 대부분의 상호작용이 모듈 내에서 이루어지고, 모듈들 사이의 상호작용은 최소한이 되어야 합니다. 만약 어떤 로직이 자신의 모듈보다 다른 모듈과 더 많이 상호작용한다면, 그 모듈로 옮겨 줍시다.
<ul><li>일부 디자인 패턴에서는 예외가 발생합니다. 예를 들어, 전략 패턴(Strategy pattern)에서는 프로그램이 실행 시간에 여러 알고리즘 중 하나를 선택할 수 있게 하는데, 알고리즘을 호출하는 부분과 알고리즘이 구현된 부분을 합쳐 버리면 빛이 바랩니다.</li></ul></li></ul>

<h3 id="3-10-데이터-뭉치" id="3-10-데이터-뭉치">3.10 데이터 뭉치</h3>
<ul><li>데이터 여러 개가 항상 함께 몰려다닌다면 구조체로 모아 줍시다. 항상 특정한 조합으로 모여 있어야 하는 데이터를 찾는 방법 중 하나는 데이터 중 하나를 없애더라도 나머지 데이터에 의미가 있는지 찾아보는 것입니다. 해당 데이터들을 연계하는 로직이나 파생되는 값 등등을 함께 클래스로 모아 주면 더욱 효과적입니다.</li></ul>

<h3 id="3-11-기본형-집착" id="3-11-기본형-집착">3.11 기본형 집착</h3>
<ul><li>화폐, 좌표, 구간, 단위를 가진 물리량 등을 적극적으로 객체로 표현합시다. 그러니까, <code>getOilPrice(unitPrice: number, distance: number): number</code>보다 <code>getOilPrice(unitPrice: Currency, distance: Length): Currency</code> 식으로 표현하면 미터인지 마일인지 같은 정보를 함께 넘겨줄 수도 있고 <code>toString()</code> 함수에 화폐 단위를 붙여 줄 수도 있다는 것입니다. 전화번호도 <code>&#34;+82 010-0000-0000&#34;</code> 같은 문자열로 저장하는 것보다 <code>{countryCode: 82, digits: [&#39;010&#39;, &#39;0000&#39;, &#39;0000&#39;]}</code> 식으로 저장하면 입력 정제와 출력 형식 지정이 보다 간편해집니다.</li></ul>

<h3 id="3-12-반복되는-switch-문" id="3-12-반복되는-switch-문">3.12 반복되는 <code>switch</code>문</h3>

<blockquote><p>순수한 객체 지향을 신봉하는 사람들과 얘기하다 보면 주제는 곧 <code>switch</code>문의 사악함으로 흘러가기 마련이다. <em>– p.123</em></p></blockquote>
<ul><li>똑같은 조건부 로직이 반복해서 등장한다면 클래스 다형성을 사용하는 방식으로 바꿔 봅시다.</li></ul>

<h3 id="3-13-반복문" id="3-13-반복문">3.13 반복문</h3>

<blockquote><p>반복문은 프로그래밍 언어가 등장할 때부터 함께 한 핵심 프로그래밍 요소다. 하지만 이제는 1970년대에 유행하던 나팔바지나 솜털 무늬 벽지보다도 못한 존재가 됐다. <em>– p.124</em></p></blockquote>
<ul><li>사실 이제는 다들 Off-By-One Error(찾아 봐도 마땅한 한국어 역어가 나오지 않네요)에 하도 데여서 이미 반복문 사용을 자제하고 있을 거예요. <code>map()</code>, <code>reduce()</code>, <code>filter()</code> 같은 파이프라인 연산을 사용합시다.</li></ul>

<h3 id="3-16-임시-필드" id="3-16-임시-필드">3.16 임시 필드</h3>
<ul><li>특정 상황에서만 값이 설정되는 필드가 있다면 코드를 이해하기 어렵고, 예측하지 못한 <code>undefined</code>와 마주하게 됩니다. 이들을 사용되는 상황에 따라 모아서 새 클래스로 분리해 주면 코드가 보다 쉬워집니다.</li></ul>

<h3 id="3-17-메시지-체인" id="3-17-메시지-체인">3.17 메시지 체인</h3>
<ul><li><code>someInstance.getSomething().getOther().getAnother()</code> 식으로 객체 요청이 연쇄적으로 이루어진다면 자주 요청되는 객체가 무엇인지 찾아보고 <code>someInstance.getAnother()</code> 식으로 요청할 수 있게 만들거나 체인을 적절히 다른 함수로 옮겨 코드 가독성을 높여 봅시다.</li></ul>

<h3 id="3-18-중개자" id="3-18-중개자">3.18 중개자</h3>
<ul><li>다른 클래스의 메서드를 호출하는 일을 위주로 돌아가는 클래스나 함수가 있다면 수정의 중간 단계가 적어지도록 해당 클래스나 함수를 사용하는 클라이언트 코드에서 직접 대상을 호출하도록 변경합시다.</li></ul>

<h3 id="3-19-내부자-거래" id="3-19-내부자-거래">3.19 내부자 거래</h3>
<ul><li>서로 다른 모듈 사이의 상호작용을 최대한 줄이고, 한 가지 일을 하기 위해 여러 모듈이 상호작용하는 상황이라면 그 일을 처리하는 모듈을 새로 만들거나 중재자 역할을 할 모듈을 새로 만듭시다. 클래스의 상속 구조에서 모듈 간의 의존성이 너무 높아진다면 상속 구조를 위임(delegate) 구조로 바꿔 서브클래스가 새로 정의하는 값만 담당하는 클래스를 따로 만드는 방법도 있습니다.</li></ul>

<h3 id="3-20-거대한-클래스" id="3-20-거대한-클래스">3.20 거대한 클래스</h3>
<ul><li>클래스가 너무 커서 이해하기 어렵다면 우선 중복 코드를 추출해 보고, 그래도 너무 길다면 여러 클래스로 쪼개 봅시다.</li></ul>

<h3 id="3-21-서로-다른-인터페이스의-대안-클래스들" id="3-21-서로-다른-인터페이스의-대안-클래스들">3.21 서로 다른 인터페이스의 대안 클래스들</h3>
<ul><li>유사한 맥락에서 서로 교체해 가며 사용할 수 있는 클래스들이 서로 다른 인터페이스를 구현한다면 문제가 생깁니다. 이 클래스들이 같은 인터페이스들 구현하도록 두 인터페이스 사이의 차이를 개별 클래스에서 구현하도록 바꾼 다음 다시 공통된 부분을 인터페이스나 슈퍼클래스로 올려 봅시다.</li></ul>

<h3 id="3-22-데이터-클래스" id="3-22-데이터-클래스">3.22 데이터 클래스</h3>
<ul><li>가변 데이터 클래스를 사용하는 클래스에서 부작용이 일어나지 않도록 <code>public</code> 필드 사용을 자제하고, 변경하면 안 되는 값들의 세터를 제거합시다. 한편 데이터 클래스를 사용하는 동작이 다른 곳에 정의되어 있다면 동작 코드를 클래스 안으로 가져올 수도 있습니다.</li></ul>

<h3 id="3-23-상속-포기" id="3-23-상속-포기">3.23 상속 포기</h3>
<ul><li>서브클래스가 부모 클래스의 구현을 따르지 않는 경우 상속 구조가 잘못되어 있다는 의미일 가능성이 높습니다. 서브클래스에서 사용하지 않는 구현은 별도의 서브클래스로 옮겨 부모 클래스에는 공통된 부분만 남도록 합시다. 한편 서브클래스가 부모 클래스의 인터페이스를 따르지 않는 경우 상속 자체가 어울리지 않는 경우일 수 있으니 위임(delegate) 클래스로 바꿔 봅시다.</li></ul>

<h3 id="3-24-주석" id="3-24-주석">3.24 주석</h3>

<blockquote><p>주석을 남겨야겠다는 생각이 들면, 가장 먼저 주석이 필요 없는 코드로 리팩터링해본다. <em>– p.131</em></p></blockquote>
<ul><li>동작에 대한 설명, 진행 상황, <code>//TODO</code>, <code>//FIXME</code> 등의 주석은 도움이 되지만, 주석이 너무 장황해진다면 코드 자체가 문제일 가능성이 높습니다.</li></ul>

<hr>
<ul><li>태그: <a href="https://blog.heartade.dev/tag:%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81" class="hashtag"><span>#</span><span class="p-category">리팩터링</span></a> <a href="https://blog.heartade.dev/tag:%EA%B3%B5%EB%B6%80" class="hashtag"><span>#</span><span class="p-category">공부</span></a> <a href="https://blog.heartade.dev/tag:%EC%8A%A4%ED%84%B0%EB%94%94" class="hashtag"><span>#</span><span class="p-category">스터디</span></a></li></ul>



<hr>

<h3 id="dani-soohan-park-heartade" id="dani-soohan-park-heartade">Dani Soohan Park (@heartade)</h3>

<p>Follow this blog at Fediverse:
<a href="https://blog.heartade.dev/heartade"><a href="https://blog.heartade.dev/@/heartade@blog.heartade.dev" class="u-url mention">@<span>heartade@blog.heartade.dev</span></a></a></p>

<p>Follow my shorter shoutouts at Fediverse:
<a href="https://social.silicon.moe/@heartade"><a href="https://blog.heartade.dev/@/heartade@social.silicon.moe" class="u-url mention">@<span>heartade@social.silicon.moe</span></a></a></p>

<p>Follow me at Bluesky:
<a href="https://bsky.app/profile/did:plc:e4a32z23pazq5dxnucj6wpee">@heartade.dev</a></p>
]]></content:encoded>
      <guid>https://blog.heartade.dev/ripaegteoring-2pan-seuteodi-3jang-kodeueseo-naneun-agcwi</guid>
      <pubDate>Wed, 18 Jan 2023 03:00:00 +0000</pubDate>
    </item>
    <item>
      <title>리팩터링 2판 스터디: 9장 &#39;데이터 조직화&#39;</title>
      <link>https://blog.heartade.dev/ripaegteoring-2pan-seuteodi-9jang-deiteo-jojighwa</link>
      <description>&lt;![CDATA[이 글은 리팩터링 2판 스터디 시리즈 중 하나입니다.&#xA;&#xA;리팩터링 2판 스터디의 두 번째 글입니다. 책을 순서대로 읽기보다 관심도 순서대로(그래야 중간에 때려치더라도 제가 관심있는 부분을 최대한 많이 정리해 놓을 수 있죠!) 매번 한 챕터씩 정리하는 식으로 진행하려고 합니다.&#xA;&#xA;그리고 생각보다 엄청나게 분량이 많더라고요! 여기에는 간단히 각 리팩터링 기법의 절차와 요점만 요약해 놓았지만, 책에서 설명해 주는 방대한 예시 없이 이 내용만 읽어서는 이해가 어려울 수 있으니 책을 읽어 보시는 것을 추천합니다. 책을 읽고 나서 나중에 필요한 기법만 찾아볼 때는 이 정리가 유용할 거예요.&#xA;&#xA;!--more--&#xA;&#xA;목차&#xA;&#xA; 정리&#xA;   9.1 변수 쪼개기&#xA;   9.2 필드 이름 바꾸기&#xA;   9.3 파생 변수를 질의 함수로 바꾸기&#xA;   9.4 참조를 값으로 바꾸기&#xA;   9.5 값을 참조로 바꾸기&#xA;   9.6 매직 리터럴 바꾸기sup1/sup&#xA; 각주&#xA;&#xA;정리&#xA;9.1 변수 쪼개기&#xA;&#xA;한 개의 변수가 여러 가지 역할을 수행한다면 코드를 이해하기 어렵게 되고, 수정하기도 어려워집니다. 이럴 때는 먼저 변수를 역할에 따라 쪼개야 합니다.&#xA;&#xA;절차 (p.330)&#xA;&#xA;변수를 선언한 곳과 값을 처음 대입하는 곳에서 변수 이름을 바꾼다.&#xA;가능하면 이 때 불변으로 선언한다.&#xA;이 변수에 두 번째로 값을 대입하는 곳 앞까지의 모든 참조를 새로운 변수 이름으로 바꾼다.&#xA;두 번째 대입 시 변수를 원래 이름으로 다시 선언한다.&#xA;테스트한다.&#xA;마지막 대입까지 반복한다.&#xA;&#xA;요점&#xA;&#xA;  역할이 둘 이상인 변수가 있다면 쪼개야 한다. 예외는 없다. 역할 하나당 변수 하나다. (p.330)&#xA;&#xA; 하나의 변수(매개변수 포함)가 한 가지 역할만 수행하는 것이 중요합니다.&#xA; 하나의 변수에 값이 여러 번 대입된다면 여러 역할을 수행한다는 신호입니다.&#xA;   단, 루프 변수, 수집 변수 등은 여러 번 대입되는 것이 자연스럽죠.&#xA;   루프 변수, 수집 변수도 코드에서 여러 역할을 수행한다면 쪼개야 합니다.&#xA;&#xA;9.2 필드 이름 바꾸기&#xA;&#xA;필드의 이름을 이해하기 쉽게 바꿔야 할 때가 있습니다. 그럴 때 아래의 방법을 사용합니다.&#xA;&#xA;절차 (p.334)&#xA;&#xA;레코드의 유효 범위가 제한적이라면 필드에 접근하는 모든 코드를 수정한 후 테스트한다. 이후 단계는 필요 없다.&#xA;레코드가 캡슐화되지 않았다면 우선 레코드를 캡슐화(7.1절)한다.&#xA;캡슐화된 객체 안의 private 필드명을 변경하고, 그에 맞게 내부 메서드들을 수정한다.&#xA;테스트한다.&#xA;생성자의 매개변수 중 필드와 이름이 겹치는 게 있다면 함수 선언 바꾸기(6.5절)로 변경한다.&#xA;접근자들의 이름도 바꿔준다(6.5절).&#xA;&#xA;사실 요즘은 위와 같은 절차를 따르지 않아도 IDE에서 이 작업을 간단하게 처리해 주죠. 하지만 사용 범위가 넓을수록 레코드를 캡슐화하는 게 좋다는 제안은 여전히 유효합니다.&#xA;&#xA;요점&#xA;&#xA;  이름은 중요하다. 그리고 프로그램 곳곳에서 쓰이는 레코드 구조체의 필드 이름들은 특히 더 중요하다. [...] 데이터 구조는 무슨 일이 벌어지는지를 이해하는&#xA;열쇠다. (p.334)&#xA;&#xA; 레코드의 유효 범위가 넓다면 클래스로 캡슐화해 봅시다.&#xA;&#xA;하나의 데이터가 게터, 세터, 생성자, 내부 필드로 나뉘기 때문에 변경할 사항이 늘어나지만, 각각을 따로 수정하면서 한 번 변경할 때마다 더 적은 부분을 건드릴 수 있기 때문에 더 안전합니다.&#xA;&#xA;상황에 따라 레코드를 캡슐화할지 아니면 단순히 속성(property)의 이름을 바꿀지 결정합시다.&#xA;&#xA; 불변 데이터 구조를 사용하면 캡슐화하는 대신 기존의 값을 복제해 새로운 이름으로 선언한 다음 사용하는 곳을 찾아 바꾸기만 하면 됩니다.&#xA;&#xA;불변 데이터 구조(immutable data structure)의 개념을 제가 정확히 이해하고 있는지 모르겠어요. 이 부분은 좀 더 자세히 공부해 봐야겠습니다.&#xA;&#xA;9.3 파생 변수를 질의 함수로 바꾸기&#xA;&#xA;파생 변수(derived variable)란 다른 변수의 값으로부터 계산되는 변수를 의미합니다. 계산에 쓰이는 변수의 값이 바뀔 때마다 새로 계산되는 식인데, 이런 의존 구조가 복잡하다면 코드가 망가지기 쉽습니다.&#xA;&#xA;절차 (p.339)&#xA;&#xA;변수 값이 갱신되는 지점을 모두 찾는다. 필요하면 변수 쪼개기를 활용해 각 갱신 지점에서 변수를 분리한다.&#xA;해당 변수의 값을 계산해주는 함수를 만든다.&#xA;해당 변수가 사용되는 모든 곳에 어서션(assertion)을 추가(10.6절)하여 함수의 계산 결과가 변수의 값과 같은지 확인한다.&#xA;   필요하면 변수 캡슐화하기(6.6절)를 적용하여 어서션이 들어갈 장소를 마련해준다.&#xA;테스트한다.&#xA;변수를 읽는 코드를 모두 함수 호출로 대체한다.&#xA;테스트한다.&#xA;변수를 선언하고 갱신하는 코드를 죽은 코드 제거하기(8.9절)로 없앤다.&#xA;&#xA;파생 변수를 쓸 때 어떤 오류가 발생한다는 것인지 쉽게 이해가 되지 않을 수 있는데, 예를 들어 다음과 같은 클래스를 생각해 봅시다.&#xA;&#xA;class Name {&#xA;  #given = &#34;&#34;;&#xA;  set given(val) {&#xA;    this.#given = val;&#xA;    this.#fullName = val + &#34; &#34; + this.#family;&#xA;  }&#xA;  get given() {&#xA;    return this.#given;&#xA;  }&#xA;  #family = &#34;&#34;;&#xA;  set family(val) {&#xA;    this.#family = val;&#xA;    this.#fullName = this.#given + &#34; &#34; + val;&#xA;  }&#xA;  get family() {&#xA;    return this.#family;&#xA;  }&#xA;  #fullName = &#34;&#34;; // 파생 변수&#xA;  get fullName() {&#xA;    return this.#fullName;&#xA;  }&#xA;}&#xA;&#xA;let gildong = new Name();&#xA;gildong.given = &#34;Gildong&#34;;&#xA;gildong.family = &#34;Hong&#34;;&#xA;console.log(gildong.fullName); // &#34;Gildong Hong&#34;&#xA;&#xA;일단은 잘 동작하지만, 클래스를 고치다가 실수로 세터 함수를 쓰지 않고 직접 #given이나 #family를 할당한다면 대참사가 벌어집니다.sup2/sup&#xA;&#xA;class Name {&#xA;  #given = &#34;&#34;;&#xA;  set given(val) {&#xA;    this.#given = val;&#xA;    this.fullName = val + &#34; &#34; + this.#family;&#xA;  }&#xA;  get given() {&#xA;    return this.#given;&#xA;  }&#xA;  #family = &#34;&#34;;&#xA;  set family(val) {&#xA;    this.#family = val;&#xA;    this.fullName = this.#given + &#34; &#34; + val;&#xA;  }&#xA;  get family() {&#xA;    return this.#family;&#xA;  }&#xA;  #fullName = &#34;&#34;; // 파생 변수&#xA;  get fullName() {&#xA;    return this.#fullName;&#xA;  }&#xA;  constructor(given, family) {&#xA;    this.#given = given;&#xA;    this.#family = family; // fullName을 업데이트하는 걸 까먹음!&#xA;  }&#xA;}&#xA;&#xA;let gildong = new Name(&#34;Gildong&#34;, &#34;Hong&#34;);&#xA;console.log(gildong.fullName); // empty string&#xA;&#xA;이 계산이 복잡하지 않다면 파생 변수를 질의 함수(query)로 바꾸는 것도 좋은 방법입니다.&#xA;&#xA;class Name {&#xA;  #given = &#34;&#34;;&#xA;  set given(val) {&#xA;    this.#given = val;&#xA;  }&#xA;  get given() {&#xA;    return this.#given;&#xA;  }&#xA;  #family = &#34;&#34;;&#xA;  set family(val) {&#xA;    this.#family = val;&#xA;  }&#xA;  get family() {&#xA;    return this.#family;&#xA;  }&#xA;  get fullName() {&#xA;    return this.#given + &#34; &#34; + this.#family;&#xA;  }&#xA;  constructor(given, family) {&#xA;    this.#given = given;&#xA;    this.#family = family; // fullName을 업데이트하는 걸 까먹음!&#xA;  }&#xA;}&#xA;&#xA;let gildong = new Name(&#34;Gildong&#34;, &#34;Hong&#34;);&#xA;console.log(gildong.fullName); // &#34;Gildong Hong&#34;&#xA;&#xA;요점&#xA;&#xA;  가변 데이터의 유효 범위를 가능한 한 좁혀야 한다고 힘주어 주장해본다.&#xA;  효과가 좋은 방법으로, 값을 쉽게 계산해낼 수 있는 변수들을 모두 제거할 수 있다. 계산 과정을 보여주는 코드 자체가 데이터의 의미를 더 분명히 드러내는 경우도 자주 있으며 변경된 값을 깜빡하고 결과 변수에 반영하지 않는 실수를 막아준다. (p.338)&#xA;&#xA; 계산이 간단한 파생 변수들을 제거하면 오류를 줄이는 데에 도움이 됩니다.&#xA; 피연산자 데이터가 불변이라면 계산 결과도 일정하므로, 새로운 데이터 구조를 생성하는 변형 연산(transformation operation)이라면 질의 함수로 대체하지 않아도 됩니다.&#xA;    주어진 기존 데이터를 기반으로 계산값을 필드로 저장하는 객체나, 데이터 구조를 다른 데이터 구조로 변환하는 함수가 해당합니다.&#xA;    예를 들어, 벡터를 만드는 함수 const vector = (x, y) =  ({x, y, size: Math.sqrt(x  x + y  y)}) 가 있고, 벡터의 x, y 값을 바꿀 일이 없다면 (즉 벡터가 불변이라면) 굳이 const vector = (x, y) =  ({x, y, get size() {return Math.sqrt(x  x + y  y)}}) 식으로 바꿀 필요는 없다는 뜻입니다.&#xA; 파생 변수의 초기값이 주어진다면 해당 초기값은 다른 변수로 쪼개어 보관합니다.&#xA;&#xA;9.4 참조를 값으로 바꾸기&#xA;&#xA;객체 내부에서 다른 객체를 사용할 때, 해당 객체를 참조(reference)로 취급하면 원본 객체의 값이 변할 때 그 객체를 참조하는 모든 곳이 영향을 받습니다. 여러 곳에서 한 객체의 상태를 공유해야 하거나 객체의 값이 자주 갱신되는 상황이 아니라면, 객체를 값(value)으로 취급하도록 코드를 변경하는 쪽이 예기치 못한 오류를 줄여 줍니다.&#xA;&#xA;절차 (p.344)&#xA;&#xA;후보 클래스가 불변인지, 혹은 불변이 될 수 있는지 확인한다.&#xA;각각의 세터를 하나씩 제거(11.7절)한다.&#xA;이 값 객체의 필드들을 사용하는 동치성(equality) 비교 메서드를 만든다.&#xA;   대부분의 언어는 이런 상황에 사용할 수 있도록 오버라이딩 가능한 동치성 비교 메서드를 제공한다.&#xA;&#xA;요점&#xA;&#xA;  값 객체는 대체로 자유롭게 활용하기 좋은데, 특히 불변이기 때문이다. 일반적으로 불변 데이터 구조는 다루기 더 쉽다. 불변 데이터 값은 프로그램 외부로 건네줘도 나중에 그 값이 나 몰래 바뀌어서 내부에 영향을 줄까 염려하지 않아도 된다. (p.343)&#xA;&#xA; 값 객체의 정의, 그리고 값 객체의 사용에 대해서는 저자 홈페이지에 따로 상세히 기재되어 있습니다.&#xA; 자바스크립트에서는 객체들의 값의 동치성을 테스트하기가 비교적 어렵지만, 동치성을 평가하는 메서드를 따로 작성하면 도움이 됩니다.&#xA;   아시다시피 ({}) === ({})은 false이기 때문인데, 객체들의 JSON.stringify() 값을 비교하는 방법도 있긴 하지만... 속도가 중요하다면 비교 메서드를 따로 작성하는 게 낫겠죠.&#xA;   자바 등에서는 동치성 평가 메서드(자바에서는 Object.equals())를 오버라이딩할 수 있는데, 객체의 해시코드를 기반으로 동작하는 컬렉션(대표적으로 HashMap)들이 있기 때문에 해시코드 생성 메서드(Object.hashCode())도 오버라이딩해야 합니다.&#xA;&#xA;9.5 값을 참조로 바꾸기&#xA;&#xA;바로 앞 절과 반대의 상황입니다. 객체의 상태를 여러 곳에서 공유하고, 그 값이 갱신되어야 한다면 해당 객체의 참조를 유지하는 것이 좋습니다. 클라이언트들이 지속적으로 하나의 객체를 참조할 수 있도록 단일한 저장소(repository)를 만들고, 이 저장소에서 필요한 객체의 참조를 받아올 수 있도록 합시다.&#xA;&#xA;절차 (p.348)&#xA;&#xA;같은 부류에 속하는 객체들을 보관할 저장소를 만든다.&#xA;생성자에서 이 부류의 객체들 중 특정 객체를 정확히 찾아내는 방법이 있는지 확인한다.&#xA;호스트 객체의 생성자들을 수정하여 필요한 객체를 이 저장소에서 찾도록 한다. 하나 수정할 때마다 테스트한다.&#xA;&#xA;책에서는 &#39;클라이언트&#39;와 &#39;호스트&#39;라는 표현을 사용하고 있는데, 이는 굳이 웹에서 말하는 서버-클라이언트가 아니더라도 우리가 리팩터링할 코드에서 가지고 있는 객체를 사용하는 코드나 클래스를 말합니다. &#xA;&#xA;요점&#xA;&#xA;  논리적으로 같은 데이터를 물리적으로 복제해 사용할 때 가장 크게 문제되는 상황은 그 데이터를 갱신해야 할 때다. 모든 복제본을 찾아서 빠짐없이 갱신해야 하며, 하나라도 놓치면 데이터 일관성이 깨져버린다. 이런 상황이라면 복제된 데이터들을 모두 참조로 바꿔주는 게 좋다. (p.347)&#xA;&#xA; 저장소 객체에 대해서는 저자 홈페이지에 설명되어 있습니다.&#xA; 저장소 객체를 사용한다면 해당 객체를 사용하는 곳에서 자신이 참조할 객체를 찾을 수 있는 구분자(identifier)가 필요합니다.&#xA; 객체에서 저장소 전역 변수를 사용하는 게 꺼림칙하다면 저장소를 생성자의 매개변수로 전달하도록 수정합시다.&#xA;   책의 각주에서 &#39;생성자 주입(Constructor Injection)&#39; 패턴을 언급하고 있는데, React 등을 다루신다면 제공자(Provider) 패턴이나 고차원 컴포넌트(Higher Order Component; HOC) 패턴이 더 익숙할 거예요. Context.Provider나 styled-components의 withTheme() 등등이 해당합니다.&#xA;&#xA;9.6 매직 리터럴 바꾸기&#xA;&#xA;매직 리터럴은 소스 코드에서 자주 사용하는 일반적인 리터럴 값을 말하는데, 3.141592 같은 것이 대표적이죠. 아니면 부가세율 0.1 같은 것일지도 모릅니다. 전자와 같은 경우는 거의 바뀌지 않겠지만, 후자는 조금 문제가 있습니다. 부가세율이 바뀌어 상품 가격의 부가세를 계산하는 모든 코드가 먹통이 될 수도 있고, 다른 팀원이 도대체 왜 온갖 곳에 price  0.1 같은 코드가 산적해 있는지 이해하지 못할 수도 있죠. 웬만하면 매직 리터럴을 상수로 따로 선언해 주도록 합시다.&#xA;&#xA;절차 (p.352)&#xA;&#xA;상수를 선언하고 매직 리터럴을 대입한다.&#xA;해당 리터럴이 사용되는 곳을 모두 찾는다.&#xA;찾은 곳 각각에서 리터럴이 새 상수와 똑같은 의미로 쓰였는지 확인하여, 같은 의미라면 상수로 대체한 후 테스트한다.&#xA;&#xA;요점&#xA;&#xA;  의미를 알고 있다고 해도 결국 각자의 머리에서 해석해낸 것일 뿐이라서, 이보다는 코드 자체가 뜻을 분명하게 드러내는 게 좋다. 상수를 정의하고 숫자 대신 상수를 사용하도록 바꾸면 될 것이다. (p.351)&#xA;&#xA; 비교 로직에 자주 쓰이는 상수라면 함수로 바꾸는 것도 좋습니다. 만약 if(user.age &lt; 19) // 미성년 같은 코드가 자주 쓰인다면, if(user.age &lt; AdultAge) 식으로 바꿀 수도 있지만 if(isMinor(user)) 식으로 바꿀 수도 있죠.&#xA; 코드 한 곳에서만 쓰이는 값이며 코드에서 의미를 유추할 수 있거나, 특별한 의미가 없는 값이라면 굳이 상수로 선언할 필요가 없습니다. 예를 들어, 원의 지름을 구할 때 diameter = radius  2에서 2를 상수로 선언하지는 않아도 됩니다.&#xA;&#xA;각주&#xA;&#xA;1&#xA;9.6절은 원서 2판에는 포함되어 있지 않다고 합니다. 웹 버전에 있다고 하네요.&#xA;&#xA;2&#xA;앞에 #을 붙여서 private 필드를 선언하는 기능은 ES2022에 추가되었습니다. &#xA;&#xA;---&#xA; 태그: #리팩터링 #공부 #스터디&#xA;&#xA;---&#xA;&#xA;!--more--&#xD;&#xA;---&#xD;&#xA;Dani Soohan Park (@heartade)&#xD;&#xA;&#xD;&#xA;Follow this blog at Fediverse:&#xD;&#xA;@heartade@blog.heartade.dev&#xD;&#xA;&#xD;&#xA;Follow my shorter shoutouts at Fediverse:&#xD;&#xA;@heartade@social.silicon.moe&#xD;&#xA;&#xD;&#xA;Follow me at Bluesky:&#xD;&#xA;@heartade.dev]]&gt;</description>
      <content:encoded><![CDATA[<p>이 글은 <a href="https://blog.heartade.dev/ripaegteoring-2pan-seuteodi-galmuri">리팩터링 2판 스터디</a> 시리즈 중 하나입니다.</p>

<p>리팩터링 2판 스터디의 두 번째 글입니다. 책을 순서대로 읽기보다 관심도 순서대로(그래야 중간에 때려치더라도 제가 관심있는 부분을 최대한 많이 정리해 놓을 수 있죠!) 매번 한 챕터씩 정리하는 식으로 진행하려고 합니다.</p>

<p>그리고 생각보다 엄청나게 분량이 많더라고요! 여기에는 간단히 각 리팩터링 기법의 절차와 요점만 요약해 놓았지만, 책에서 설명해 주는 방대한 예시 없이 이 내용만 읽어서는 이해가 어려울 수 있으니 책을 읽어 보시는 것을 추천합니다. 책을 읽고 나서 나중에 필요한 기법만 찾아볼 때는 이 정리가 유용할 거예요.</p>



<h2 id="목차">목차</h2>
<ul><li><a href="#%EC%A0%95%EB%A6%AC">정리</a>
<ul><li><a href="#9-1-%EB%B3%80%EC%88%98-%EC%AA%BC%EA%B0%9C%EA%B8%B0">9.1 변수 쪼개기</a></li>
<li><a href="#9-2-%ED%95%84%EB%93%9C-%EC%9D%B4%EB%A6%84-%EB%B0%94%EA%BE%B8%EA%B8%B0">9.2 필드 이름 바꾸기</a></li>
<li><a href="#9-3-%ED%8C%8C%EC%83%9D-%EB%B3%80%EC%88%98%EB%A5%BC-%EC%A7%88%EC%9D%98-%ED%95%A8%EC%88%98%EB%A1%9C-%EB%B0%94%EA%BE%B8%EA%B8%B0">9.3 파생 변수를 질의 함수로 바꾸기</a></li>
<li><a href="#9-4-%EC%B0%B8%EC%A1%B0%EB%A5%BC-%EA%B0%92%EC%9C%BC%EB%A1%9C-%EB%B0%94%EA%BE%B8%EA%B8%B0">9.4 참조를 값으로 바꾸기</a></li>
<li><a href="#9-5-%EA%B0%92%EC%9D%84-%EC%B0%B8%EC%A1%B0%EB%A1%9C-%EB%B0%94%EA%BE%B8%EA%B8%B0">9.5 값을 참조로 바꾸기</a></li>
<li><a href="#9-6-%EB%A7%A4%EC%A7%81-%EB%A6%AC%ED%84%B0%EB%9F%B4-%EB%B0%94%EA%BE%B8%EA%B8%B0">9.6 매직 리터럴 바꾸기</a><sup><a href="#1">1</a></sup></li></ul></li>
<li><a href="#%EA%B0%81%EC%A3%BC">각주</a></li></ul>

<h2 id="정리">정리</h2>

<h3 id="9-1-변수-쪼개기" id="9-1-변수-쪼개기">9.1 변수 쪼개기</h3>

<p>한 개의 변수가 여러 가지 역할을 수행한다면 코드를 이해하기 어렵게 되고, 수정하기도 어려워집니다. 이럴 때는 먼저 변수를 역할에 따라 쪼개야 합니다.</p>

<h4 id="절차-p-330" id="절차-p-330">절차 (p.330)</h4>
<ol><li>변수를 선언한 곳과 값을 처음 대입하는 곳에서 변수 이름을 바꾼다.</li>
<li>가능하면 이 때 불변으로 선언한다.</li>
<li>이 변수에 두 번째로 값을 대입하는 곳 앞까지의 모든 참조를 새로운 변수 이름으로 바꾼다.</li>
<li>두 번째 대입 시 변수를 원래 이름으로 다시 선언한다.</li>
<li>테스트한다.</li>
<li>마지막 대입까지 반복한다.</li></ol>

<h4 id="요점">요점</h4>

<blockquote><p>역할이 둘 이상인 변수가 있다면 쪼개야 한다. 예외는 없다. 역할 하나당 변수 하나다. (p.330)</p></blockquote>
<ul><li>하나의 변수(매개변수 포함)가 한 가지 역할만 수행하는 것이 중요합니다.</li>
<li>하나의 변수에 값이 여러 번 대입된다면 여러 역할을 수행한다는 신호입니다.
<ul><li>단, 루프 변수, 수집 변수 등은 여러 번 대입되는 것이 자연스럽죠.</li>
<li>루프 변수, 수집 변수도 코드에서 여러 역할을 수행한다면 쪼개야 합니다.</li></ul></li></ul>

<h3 id="9-2-필드-이름-바꾸기" id="9-2-필드-이름-바꾸기">9.2 필드 이름 바꾸기</h3>

<p>필드의 이름을 이해하기 쉽게 바꿔야 할 때가 있습니다. 그럴 때 아래의 방법을 사용합니다.</p>

<h4 id="절차-p-334" id="절차-p-334">절차 (p.334)</h4>
<ol><li>레코드의 유효 범위가 제한적이라면 필드에 접근하는 모든 코드를 수정한 후 테스트한다. 이후 단계는 필요 없다.</li>
<li>레코드가 캡슐화되지 않았다면 우선 레코드를 캡슐화(7.1절)한다.</li>
<li>캡슐화된 객체 안의 private 필드명을 변경하고, 그에 맞게 내부 메서드들을 수정한다.</li>
<li>테스트한다.</li>
<li>생성자의 매개변수 중 필드와 이름이 겹치는 게 있다면 함수 선언 바꾸기(6.5절)로 변경한다.</li>
<li>접근자들의 이름도 바꿔준다(6.5절).</li></ol>

<p>사실 요즘은 위와 같은 절차를 따르지 않아도 IDE에서 이 작업을 간단하게 처리해 주죠. 하지만 사용 범위가 넓을수록 레코드를 캡슐화하는 게 좋다는 제안은 여전히 유효합니다.</p>

<h4 id="요점-1" id="요점-1">요점</h4>

<blockquote><p>이름은 중요하다. 그리고 프로그램 곳곳에서 쓰이는 레코드 구조체의 필드 이름들은 특히 더 중요하다. [...] 데이터 구조는 무슨 일이 벌어지는지를 이해하는
열쇠다. (p.334)</p></blockquote>
<ul><li>레코드의 유효 범위가 넓다면 클래스로 캡슐화해 봅시다.</li></ul>

<p>하나의 데이터가 게터, 세터, 생성자, 내부 필드로 나뉘기 때문에 변경할 사항이 늘어나지만, 각각을 따로 수정하면서 한 번 변경할 때마다 더 적은 부분을 건드릴 수 있기 때문에 더 안전합니다.</p>

<p>상황에 따라 레코드를 캡슐화할지 아니면 단순히 속성(property)의 이름을 바꿀지 결정합시다.</p>
<ul><li>불변 데이터 구조를 사용하면 캡슐화하는 대신 기존의 값을 복제해 새로운 이름으로 선언한 다음 사용하는 곳을 찾아 바꾸기만 하면 됩니다.</li></ul>

<p>불변 데이터 구조(immutable data structure)의 개념을 제가 정확히 이해하고 있는지 모르겠어요. 이 부분은 좀 더 자세히 공부해 봐야겠습니다.</p>

<h3 id="9-3-파생-변수를-질의-함수로-바꾸기" id="9-3-파생-변수를-질의-함수로-바꾸기">9.3 파생 변수를 질의 함수로 바꾸기</h3>

<p>파생 변수(derived variable)란 다른 변수의 값으로부터 계산되는 변수를 의미합니다. 계산에 쓰이는 변수의 값이 바뀔 때마다 새로 계산되는 식인데, 이런 의존 구조가 복잡하다면 코드가 망가지기 쉽습니다.</p>

<h4 id="절차-p-339" id="절차-p-339">절차 (p.339)</h4>
<ol><li>변수 값이 갱신되는 지점을 모두 찾는다. 필요하면 변수 쪼개기를 활용해 각 갱신 지점에서 변수를 분리한다.</li>
<li>해당 변수의 값을 계산해주는 함수를 만든다.</li>
<li>해당 변수가 사용되는 모든 곳에 어서션(assertion)을 추가(10.6절)하여 함수의 계산 결과가 변수의 값과 같은지 확인한다.
<ul><li>필요하면 변수 캡슐화하기(6.6절)를 적용하여 어서션이 들어갈 장소를 마련해준다.</li></ul></li>
<li>테스트한다.</li>
<li>변수를 읽는 코드를 모두 함수 호출로 대체한다.</li>
<li>테스트한다.</li>
<li>변수를 선언하고 갱신하는 코드를 죽은 코드 제거하기(8.9절)로 없앤다.</li></ol>

<p>파생 변수를 쓸 때 어떤 오류가 발생한다는 것인지 쉽게 이해가 되지 않을 수 있는데, 예를 들어 다음과 같은 클래스를 생각해 봅시다.</p>

<pre><code class="language-javascript">class Name {
  #given = &#34;&#34;;
  set given(val) {
    this.#given = val;
    this.#fullName = val + &#34; &#34; + this.#family;
  }
  get given() {
    return this.#given;
  }
  #family = &#34;&#34;;
  set family(val) {
    this.#family = val;
    this.#fullName = this.#given + &#34; &#34; + val;
  }
  get family() {
    return this.#family;
  }
  #fullName = &#34;&#34;; // 파생 변수
  get fullName() {
    return this.#fullName;
  }
}

let gildong = new Name();
gildong.given = &#34;Gildong&#34;;
gildong.family = &#34;Hong&#34;;
console.log(gildong.fullName); // &#34;Gildong Hong&#34;
</code></pre>

<p>일단은 잘 동작하지만, 클래스를 고치다가 실수로 세터 함수를 쓰지 않고 직접 <code>#given</code>이나 <code>#family</code>를 할당한다면 대참사가 벌어집니다.<sup><a href="#2">2</a></sup></p>

<pre><code class="language-javascript">class Name {
  #given = &#34;&#34;;
  set given(val) {
    this.#given = val;
    this.fullName = val + &#34; &#34; + this.#family;
  }
  get given() {
    return this.#given;
  }
  #family = &#34;&#34;;
  set family(val) {
    this.#family = val;
    this.fullName = this.#given + &#34; &#34; + val;
  }
  get family() {
    return this.#family;
  }
  #fullName = &#34;&#34;; // 파생 변수
  get fullName() {
    return this.#fullName;
  }
  constructor(given, family) {
    this.#given = given;
    this.#family = family; // fullName을 업데이트하는 걸 까먹음!
  }
}

let gildong = new Name(&#34;Gildong&#34;, &#34;Hong&#34;);
console.log(gildong.fullName); // &lt;empty string&gt;
</code></pre>

<p>이 계산이 복잡하지 않다면 파생 변수를 질의 함수(query)로 바꾸는 것도 좋은 방법입니다.</p>

<pre><code class="language-javascript">class Name {
  #given = &#34;&#34;;
  set given(val) {
    this.#given = val;
  }
  get given() {
    return this.#given;
  }
  #family = &#34;&#34;;
  set family(val) {
    this.#family = val;
  }
  get family() {
    return this.#family;
  }
  get fullName() {
    return this.#given + &#34; &#34; + this.#family;
  }
  constructor(given, family) {
    this.#given = given;
    this.#family = family; // fullName을 업데이트하는 걸 까먹음!
  }
}

let gildong = new Name(&#34;Gildong&#34;, &#34;Hong&#34;);
console.log(gildong.fullName); // &#34;Gildong Hong&#34;
</code></pre>

<h4 id="요점-2" id="요점-2">요점</h4>

<blockquote><p>가변 데이터의 유효 범위를 가능한 한 좁혀야 한다고 힘주어 주장해본다.
효과가 좋은 방법으로, 값을 쉽게 계산해낼 수 있는 변수들을 모두 제거할 수 있다. 계산 과정을 보여주는 코드 자체가 데이터의 의미를 더 분명히 드러내는 경우도 자주 있으며 변경된 값을 깜빡하고 결과 변수에 반영하지 않는 실수를 막아준다. (p.338)</p></blockquote>
<ul><li>계산이 간단한 파생 변수들을 제거하면 오류를 줄이는 데에 도움이 됩니다.</li>
<li>피연산자 데이터가 불변이라면 계산 결과도 일정하므로, 새로운 데이터 구조를 생성하는 변형 연산(transformation operation)이라면 질의 함수로 대체하지 않아도 됩니다.
<ul><li>주어진 기존 데이터를 기반으로 계산값을 필드로 저장하는 객체나, 데이터 구조를 다른 데이터 구조로 변환하는 함수가 해당합니다.</li>
<li>예를 들어, 벡터를 만드는 함수 <code>const vector = (x, y) =&gt; ({x, y, size: Math.sqrt(x * x + y * y)})</code> 가 있고, 벡터의 <code>x</code>, <code>y</code> 값을 바꿀 일이 없다면 (즉 벡터가 불변이라면) 굳이 <code>const vector = (x, y) =&gt; ({x, y, get size() {return Math.sqrt(x * x + y * y)}})</code> 식으로 바꿀 필요는 없다는 뜻입니다.</li></ul></li>
<li>파생 변수의 초기값이 주어진다면 해당 초기값은 다른 변수로 쪼개어 보관합니다.</li></ul>

<h3 id="9-4-참조를-값으로-바꾸기" id="9-4-참조를-값으로-바꾸기">9.4 참조를 값으로 바꾸기</h3>

<p>객체 내부에서 다른 객체를 사용할 때, 해당 객체를 참조(reference)로 취급하면 원본 객체의 값이 변할 때 그 객체를 참조하는 모든 곳이 영향을 받습니다. 여러 곳에서 한 객체의 상태를 공유해야 하거나 객체의 값이 자주 갱신되는 상황이 아니라면, 객체를 값(value)으로 취급하도록 코드를 변경하는 쪽이 예기치 못한 오류를 줄여 줍니다.</p>

<h4 id="절차-p-344" id="절차-p-344">절차 (p.344)</h4>
<ol><li>후보 클래스가 불변인지, 혹은 불변이 될 수 있는지 확인한다.</li>
<li>각각의 세터를 하나씩 제거(11.7절)한다.</li>
<li>이 값 객체의 필드들을 사용하는 동치성(equality) 비교 메서드를 만든다.
<ul><li>대부분의 언어는 이런 상황에 사용할 수 있도록 오버라이딩 가능한 동치성 비교 메서드를 제공한다.</li></ul></li></ol>

<h4 id="요점-3" id="요점-3">요점</h4>

<blockquote><p>값 객체는 대체로 자유롭게 활용하기 좋은데, 특히 불변이기 때문이다. 일반적으로 불변 데이터 구조는 다루기 더 쉽다. 불변 데이터 값은 프로그램 외부로 건네줘도 나중에 그 값이 나 몰래 바뀌어서 내부에 영향을 줄까 염려하지 않아도 된다. (p.343)</p></blockquote>
<ul><li>값 객체의 정의, 그리고 값 객체의 사용에 대해서는 <a href="https://martinfowler.com/bliki/ValueObject.html">저자 홈페이지에 따로 상세히 기재</a>되어 있습니다.</li>
<li>자바스크립트에서는 객체들의 값의 동치성을 테스트하기가 비교적 어렵지만, 동치성을 평가하는 메서드를 따로 작성하면 도움이 됩니다.
<ul><li>아시다시피 <code>({}) === ({})</code>은 <code>false</code>이기 때문인데, 객체들의 <code>JSON.stringify()</code> 값을 비교하는 방법도 있긴 하지만... 속도가 중요하다면 비교 메서드를 따로 작성하는 게 낫겠죠.</li>
<li>자바 등에서는 동치성 평가 메서드(자바에서는 <code>Object.equals()</code>)를 오버라이딩할 수 있는데, 객체의 해시코드를 기반으로 동작하는 컬렉션(대표적으로 <code>HashMap</code>)들이 있기 때문에 해시코드 생성 메서드(<code>Object.hashCode()</code>)도 오버라이딩해야 합니다.</li></ul></li></ul>

<h3 id="9-5-값을-참조로-바꾸기" id="9-5-값을-참조로-바꾸기">9.5 값을 참조로 바꾸기</h3>

<p>바로 앞 절과 반대의 상황입니다. 객체의 상태를 여러 곳에서 공유하고, 그 값이 갱신되어야 한다면 해당 객체의 참조를 유지하는 것이 좋습니다. 클라이언트들이 지속적으로 하나의 객체를 참조할 수 있도록 단일한 저장소(repository)를 만들고, 이 저장소에서 필요한 객체의 참조를 받아올 수 있도록 합시다.</p>

<h4 id="절차-p-348" id="절차-p-348">절차 (p.348)</h4>
<ol><li>같은 부류에 속하는 객체들을 보관할 저장소를 만든다.</li>
<li>생성자에서 이 부류의 객체들 중 특정 객체를 정확히 찾아내는 방법이 있는지 확인한다.</li>
<li>호스트 객체의 생성자들을 수정하여 필요한 객체를 이 저장소에서 찾도록 한다. 하나 수정할 때마다 테스트한다.</li></ol>

<p>책에서는 &#39;클라이언트&#39;와 &#39;호스트&#39;라는 표현을 사용하고 있는데, 이는 굳이 웹에서 말하는 서버-클라이언트가 아니더라도 우리가 리팩터링할 코드에서 가지고 있는 객체를 사용하는 코드나 클래스를 말합니다.</p>

<h4 id="요점-4" id="요점-4">요점</h4>

<blockquote><p>논리적으로 같은 데이터를 물리적으로 복제해 사용할 때 가장 크게 문제되는 상황은 그 데이터를 갱신해야 할 때다. 모든 복제본을 찾아서 빠짐없이 갱신해야 하며, 하나라도 놓치면 데이터 일관성이 깨져버린다. 이런 상황이라면 복제된 데이터들을 모두 참조로 바꿔주는 게 좋다. (p.347)</p></blockquote>
<ul><li>저장소 객체에 대해서는 <a href="https://martinfowler.com/eaaCatalog/repository.html">저자 홈페이지에 설명</a>되어 있습니다.</li>
<li>저장소 객체를 사용한다면 해당 객체를 사용하는 곳에서 자신이 참조할 객체를 찾을 수 있는 구분자(identifier)가 필요합니다.</li>
<li>객체에서 저장소 전역 변수를 사용하는 게 꺼림칙하다면 저장소를 생성자의 매개변수로 전달하도록 수정합시다.
<ul><li>책의 각주에서 &#39;생성자 주입(Constructor Injection)&#39; 패턴을 언급하고 있는데, React 등을 다루신다면 제공자(Provider) 패턴이나 고차원 컴포넌트(Higher Order Component; HOC) 패턴이 더 익숙할 거예요. <code>Context.Provider</code>나 <code>styled-components</code>의 <code>withTheme()</code> 등등이 해당합니다.</li></ul></li></ul>

<h3 id="9-6-매직-리터럴-바꾸기" id="9-6-매직-리터럴-바꾸기">9.6 매직 리터럴 바꾸기</h3>

<p>매직 리터럴은 소스 코드에서 자주 사용하는 일반적인 리터럴 값을 말하는데, <code>3.141592</code> 같은 것이 대표적이죠. 아니면 부가세율 <code>0.1</code> 같은 것일지도 모릅니다. 전자와 같은 경우는 거의 바뀌지 않겠지만, 후자는 조금 문제가 있습니다. 부가세율이 바뀌어 상품 가격의 부가세를 계산하는 모든 코드가 먹통이 될 수도 있고, 다른 팀원이 도대체 왜 온갖 곳에 <code>price * 0.1</code> 같은 코드가 산적해 있는지 이해하지 못할 수도 있죠. 웬만하면 매직 리터럴을 상수로 따로 선언해 주도록 합시다.</p>

<h4 id="절차-p-352" id="절차-p-352">절차 (p.352)</h4>
<ol><li>상수를 선언하고 매직 리터럴을 대입한다.</li>
<li>해당 리터럴이 사용되는 곳을 모두 찾는다.</li>
<li>찾은 곳 각각에서 리터럴이 새 상수와 똑같은 의미로 쓰였는지 확인하여, 같은 의미라면 상수로 대체한 후 테스트한다.</li></ol>

<h4 id="요점-5" id="요점-5">요점</h4>

<blockquote><p>의미를 알고 있다고 해도 결국 각자의 머리에서 해석해낸 것일 뿐이라서, 이보다는 코드 자체가 뜻을 분명하게 드러내는 게 좋다. 상수를 정의하고 숫자 대신 상수를 사용하도록 바꾸면 될 것이다. (p.351)</p></blockquote>
<ul><li>비교 로직에 자주 쓰이는 상수라면 함수로 바꾸는 것도 좋습니다. 만약 <code>if(user.age &lt; 19) // 미성년</code> 같은 코드가 자주 쓰인다면, <code>if(user.age &lt; AdultAge)</code> 식으로 바꿀 수도 있지만 <code>if(isMinor(user))</code> 식으로 바꿀 수도 있죠.</li>
<li>코드 한 곳에서만 쓰이는 값이며 코드에서 의미를 유추할 수 있거나, 특별한 의미가 없는 값이라면 굳이 상수로 선언할 필요가 없습니다. 예를 들어, 원의 지름을 구할 때 <code>diameter = radius * 2</code>에서 2를 상수로 선언하지는 않아도 됩니다.</li></ul>

<h2 id="각주">각주</h2>

<h4 id="1" id="1">1</h4>

<p>9.6절은 원서 2판에는 포함되어 있지 않다고 합니다. 웹 버전에 있다고 하네요.</p>

<h4 id="2" id="2">2</h4>

<p>앞에 <code>#</code>을 붙여서 private 필드를 선언하는 기능은 ES2022에 추가되었습니다.</p>

<hr>
<ul><li>태그: <a href="https://blog.heartade.dev/tag:%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81" class="hashtag"><span>#</span><span class="p-category">리팩터링</span></a> <a href="https://blog.heartade.dev/tag:%EA%B3%B5%EB%B6%80" class="hashtag"><span>#</span><span class="p-category">공부</span></a> <a href="https://blog.heartade.dev/tag:%EC%8A%A4%ED%84%B0%EB%94%94" class="hashtag"><span>#</span><span class="p-category">스터디</span></a></li></ul>

<hr>



<hr>

<h3 id="dani-soohan-park-heartade" id="dani-soohan-park-heartade">Dani Soohan Park (@heartade)</h3>

<p>Follow this blog at Fediverse:
<a href="https://blog.heartade.dev/heartade"><a href="https://blog.heartade.dev/@/heartade@blog.heartade.dev" class="u-url mention">@<span>heartade@blog.heartade.dev</span></a></a></p>

<p>Follow my shorter shoutouts at Fediverse:
<a href="https://social.silicon.moe/@heartade"><a href="https://blog.heartade.dev/@/heartade@social.silicon.moe" class="u-url mention">@<span>heartade@social.silicon.moe</span></a></a></p>

<p>Follow me at Bluesky:
<a href="https://bsky.app/profile/did:plc:e4a32z23pazq5dxnucj6wpee">@heartade.dev</a></p>
]]></content:encoded>
      <guid>https://blog.heartade.dev/ripaegteoring-2pan-seuteodi-9jang-deiteo-jojighwa</guid>
      <pubDate>Wed, 11 Jan 2023 06:00:00 +0000</pubDate>
    </item>
    <item>
      <title>리팩터링 2판 스터디: 갈무리</title>
      <link>https://blog.heartade.dev/ripaegteoring-2pan-seuteodi-galmuri</link>
      <description>&lt;![CDATA[회사에서 리팩터링(Refactoring; 마틴 파울러 지음, 남기혁 옮김) 2판 스터디를 진행하면서, 새로 배우는 내용을 블로그에 정리하고 있습니다. 이 글에 책의 전체 목차를 옮기고, 한 챕터를 정리할 때마다 링크할 예정입니다.&#xA;&#xA;!--more--&#xA;&#xA;목차&#xA;&#xA;리팩터링: 첫 번째 예시&#xA;리팩터링 원칙&#xA;코드에서 나는 악취&#xA;테스트 구축하기&#xA;리팩터링 카탈로그 보는 법&#xA;기본적인 리팩터링&#xA;캡슐화&#xA;기능 이동&#xA;데이터 조직화&#xA;10. 조건부 로직 간소화&#xA;11. API 리팩터링&#xA;12. 상속 다루기&#xA;&#xA;---&#xA;태그: #리팩터링 #리팩토링 #스터디 #공부 #코딩&#xA;&#xA;!--more--&#xD;&#xA;---&#xD;&#xA;Dani Soohan Park (@heartade)&#xD;&#xA;&#xD;&#xA;Follow this blog at Fediverse:&#xD;&#xA;@heartade@blog.heartade.dev&#xD;&#xA;&#xD;&#xA;Follow my shorter shoutouts at Fediverse:&#xD;&#xA;@heartade@social.silicon.moe&#xD;&#xA;&#xD;&#xA;Follow me at Bluesky:&#xD;&#xA;@heartade.dev]]&gt;</description>
      <content:encoded><![CDATA[<p>회사에서 리팩터링(Refactoring; 마틴 파울러 지음, 남기혁 옮김) 2판 스터디를 진행하면서, 새로 배우는 내용을 블로그에 정리하고 있습니다. 이 글에 책의 전체 목차를 옮기고, 한 챕터를 정리할 때마다 링크할 예정입니다.</p>



<h2 id="목차">목차</h2>
<ol><li><a href="https://blog.heartade.dev/ripaegteoring-2pan-seuteodi-1jang-ripaegteoring-ceos-beonjjae-yesi">리팩터링: 첫 번째 예시</a></li>
<li>리팩터링 원칙</li>
<li>코드에서 나는 악취</li>
<li>테스트 구축하기</li>
<li>리팩터링 카탈로그 보는 법</li>
<li>기본적인 리팩터링</li>
<li>캡슐화</li>
<li>기능 이동</li>
<li><a href="https://blog.heartade.dev/ripaegteoring-2pan-seuteodi-9jang-deiteo-jojighwa">데이터 조직화</a></li>
<li>조건부 로직 간소화</li>
<li>API 리팩터링</li>
<li>상속 다루기</li></ol>

<hr>
<ul><li>태그: <a href="https://blog.heartade.dev/tag:%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81" class="hashtag"><span>#</span><span class="p-category">리팩터링</span></a> <a href="https://blog.heartade.dev/tag:%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81" class="hashtag"><span>#</span><span class="p-category">리팩토링</span></a> <a href="https://blog.heartade.dev/tag:%EC%8A%A4%ED%84%B0%EB%94%94" class="hashtag"><span>#</span><span class="p-category">스터디</span></a> <a href="https://blog.heartade.dev/tag:%EA%B3%B5%EB%B6%80" class="hashtag"><span>#</span><span class="p-category">공부</span></a> <a href="https://blog.heartade.dev/tag:%EC%BD%94%EB%94%A9" class="hashtag"><span>#</span><span class="p-category">코딩</span></a></li></ul>



<hr>

<h3 id="dani-soohan-park-heartade" id="dani-soohan-park-heartade">Dani Soohan Park (@heartade)</h3>

<p>Follow this blog at Fediverse:
<a href="https://blog.heartade.dev/heartade"><a href="https://blog.heartade.dev/@/heartade@blog.heartade.dev" class="u-url mention">@<span>heartade@blog.heartade.dev</span></a></a></p>

<p>Follow my shorter shoutouts at Fediverse:
<a href="https://social.silicon.moe/@heartade"><a href="https://blog.heartade.dev/@/heartade@social.silicon.moe" class="u-url mention">@<span>heartade@social.silicon.moe</span></a></a></p>

<p>Follow me at Bluesky:
<a href="https://bsky.app/profile/did:plc:e4a32z23pazq5dxnucj6wpee">@heartade.dev</a></p>
]]></content:encoded>
      <guid>https://blog.heartade.dev/ripaegteoring-2pan-seuteodi-galmuri</guid>
      <pubDate>Wed, 11 Jan 2023 03:01:30 +0000</pubDate>
    </item>
  </channel>
</rss>