데보션앱 소개페이지 바로가기
로그인 선택

신고하기

CLOSE
신고사유 (대표 사유 1개)
상세내용 (선택)
0/200
  • 신고한 게시글은 더 이상 보이지 않습니다.
  • 이용약관과 운영정책에 따라 신고사유에 해당하는지 검토 후 조치됩니다.
  • 허위 신고인 경우, 신고자의 서비스 이용이 제한될 수 있으니 유의하시어 신중하게 신고해 주세요.
(이 회원이 작성한 모든 댓글과 커뮤니티 게시물이 보이지 않고, 알림도 오지 않습니다.)

미리보기

커뮤니티

      1,234

      badge 23.06.15

      글 등록

      카테고리를 선택해주세요.

      DEVOTEE를 활성화 시키면
      지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.

      버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.

      임시저장함에 저장되었습니다. 저장일시 : 2022.5.17 14:29:08

      임시저장함

      제목을 선택하시면 이어서 작성이 가능하며,
      최대 20건까지 저장합니다.
      컨텐츠 유형, 제목, 저장일시, 삭제로 이뤄진 임시저장 목록
      컨텐츠 유형 제목 저장일 삭제

      데보션 블로그 게재 요청

      CLOSE
      • *
      • *

      본인인증

      효율적인 데보션 서비스 이용 및
      고객님의 소중한 개인정보보호를 위해
      본인인증을 진행해주세요. 본인인증 미 진행 시 로그인이 제한됩니다.
      본인인증 실패

      본인인증 로그인에 실패하였습니다.
      회원이 아니시거나 본인인증 등록이
      완료되지 않은 사용자입니다.

      회원정보 연결

      [데보션영과 함께하는 책읽기-7편] 리팩터링 2판

      ekwoo08 23.02.21
      2,241 25 1

      안녕하세요! 데보션 영 1기 복불복 조입니다 :)

      다독다독 콘텐츠로 읽은 책을 한 권을 소개합니다.

      바로바로 더 나은 코드를 위한 리팩터링 2판입니다!!!

      선정



      7월 말에 데보션 영 컨텐츠로 다독다독 미션이 진행되었고 책 선정을 시작하였습니다.

      모두 서로 다른 분야에 관심있었고 회의를 거쳤습니다.

      그리고 모두에게 도움이 될 책인 『리팩터링 2판』을 선정하였습니다.

      8월 15일 처음 책을 받았고, 차근차근 읽기 시작했습니다.


      소개

      저자 “마틴 파울러”가 쓴 책으로, 리팩터링과 관련된 내용을 서술합니다.

      리팩터링이란 “소프트웨어의 겉보기 동작은 그대로 유지한 채, 코드를 이해하고 수정하기 쉽도록 내부 구조를 변경하는 기법”이라고 합니다.

      JS 코드들을 예시로 들고 있어 많은 도움이 되는 것 같습니다.

      큰 규모의 코드 작성을 제대로 한 적이 없던 저에게 리팩터링의 필요성과 의의를 제대로 알려준 의미깊은 책이었습니다.


      정리

      모든 챕터를 정리하기보다 가장 의미있는 1, 2챕터를 모두 읽고 추가적으로 마음에 드는 한 챕터씩 정리하였습니다.

      우진실님 작성 : 출처 Ch. 1~2


      CHAPTER 01

      리팩터링 : 첫번째 예시

      프로그램이 작동이 잘하는데 보기가 어렵다는 이유로 리팩터링을 해야한다는 것은 너무 미적기준이 아닐까 .

      컴파일러는 어차피 개의치 않는다. 리팩터링이 필요한 이유는 변경이 자주 일어날 때의 모든 함수를 바꿔줘야하는 경우가 발생하지 않도록 하기 위해서 이다.

      여러 함수와 프로그램 요소로 재구성을 하는 것이 좋은데 새로운 기능을 추가하기에 편한 구조가 아니라면, 먼저 기능을 추가하기 쉬운 형태로 리팩터링 하고 나서 원하는 기능을 추가하는 것이 좋다고 한다.

      리팩토링 직접 해보기

      책에서 제공되는 예시를 통해 리팩토링 절차를 하나씩 살펴 볼 수 있다.

      • 테스트 코드

        리팩터링의 첫 단계는 테스트 코드이다. 리팩터링할 코드를 꼼꼼하게 수정해줄 테스트 코드가 필요하기 때문이다. 테스트 코드를 작성할 때에는 테스트 결과를 보고하는 방식이 중요하다.

        이때 테스트는 반드시 자가 진단 하도록 만든다

        → 테스트 코드를 사용하면 디버깅 시간도 줄어듦으로써 작업시간도 준다.

        조금씩 변경하고 매번 테스트 하는 것이 리팩터링 절차의 핵심이다. 작은 단계로 나눠서 진행 해야 실수하더라도 버그를 쉽게 찾을 수 있다.

      • 함수 추출하기

        기존 함수에서 새로운 함수를 추출한다. 바깥 함수에서 쓰던 변수를 새로 함수를 추출한 함수에 매개변수로 전달 할 필요가 없어서 편해진다. 일반적으로 중첩 함수로 만들면 일이 하나 줄어드는 셈이다.

      • 변수 이름 명확히 바꿔보기

        변수의 역할을 쉽게 알 수 있도록 한다. 매개 변수의 역할이 뚜렷하지 않을 때에는 부정관사(a/an) 을 이용 하라고 캔트 벡씨가 말했다고 한다.

        💡 컴퓨터가 이해하는 코드는 바보도 작성할 수 있다. 사람이 이해하도록 작성하는 프로그래머가 진정한 실력자다.

      • 임시변수를 질의 함수로 바꾸기

        추출한 함수에 변수를 인라인 하여 매개변수를 제거한다. 먼저 새로만든 함수를 사용하도록 기존 함수를 수정해주고 기존 매개변수를 삭제해주면 된다.

        제대로 리팩터링된 코드베이스는 그렇지 않은 것보다 개선하기 수루얼하다. 지역변수를 제거해서 얻는 가장 큰 장점은 추출 작업이 훨씬 쉬워진다는 것이다. 유효범위를 신경 써야할 대상이 줄어들기 때문이다.

      • 반복문 쪼개기

        반복문이 중복되면 성능이 느려지지 않을까 걱정할 수 있지만 실제로 중복은 성능에 미치는 영향이 미미할 때가 많다. 똑똑한 컴파일럳르이 최신 캐싱 기법 등으로 무장하고 있어서 우리의 직관을 초월하는 결과를 내어주기 때문이다.

        → 하지만 때로는 리팩터링이 성능에 상당한 영향을 주기도 한다. 그런 경우라도 잘 다듬어진 코드야 성능 개선 작업도 훨씬 수월하기 때문에 성능이 크게 떨어졌다면 리팩터링 후 시간을 내어 성능을 개선하도록 한다.

        결과적으로 더 깔끔하면서 더 빠른 코드를 얻게 된다.

      • 단계 쪼개기

        함수를 쪼개서 첫번째 단계에서 두번째 단계로 전달할 중간 데이터 구조를 생성할 것이다. 단계를 쪼개려면 먼저 두번째 단계가 될 코드들을 함수 추출하기로 뽑아낸다.

        이렇게 코드를 추가하다보면 함수가 늘어나게 된다. 함수로 추출하면서 함수 본문을 열고 닫는 괄호가 덧붙었기 때문이다.

        추가된 코드 덕분에 전체 로직을 구성하는 요소 각각이 더 뚜렷이 부각되고 모듈화하면서 각 부분이 하는 일과 그 부분들이 맞물려 돌아가는 과정을 파악하기 쉬워진다.

        💡 캠핑자들에게는 “도착했을 때보다 깔끔하게 정돈하고 떠난다” 라는 규칙이 있다. 프로그래밍도 마찬가지다. 항시 코드베이스를 작업 시작 전보다 건강하게(healthy) 만들어놓고 떠나야 한다.

      • 다형성을 활용해 계산 코드 재구성하기

        조건부 로직은 코드 수정 횟수가 늘어날수록 골칫거리로 전락하기 쉬워 이를 방지하려면 프로그래밍 언어가 제공하는 구조적인 요소로 적절히 보완해야한다.

        💡객체지향의핵심 특성인 다형성(polymorphism)을 활용하는 것이 자연스럽다.

        * 타입코드 대신 서브 클래스로 바꾸기

        * 생성자를 팩터리 함수로 바꾸기

        * 조건부 로직을 다형성으로 바꾸기

      함수 추출하기, 변수 인라인하기, 함수 옮기기, 조건부 로직을 다형성으로 바꾸기 등 다양한 리팩터링 기법을 알 수 있었다.

      리팩터링은 대부분 코드가 하는 일을 파악하는 데서 시작한다. 그래서 코드를 읽고 개선점을 찾고 리팩터링을 하면서 개선점을 코드에 반영한다.

      💡 좋은 코드를 가늠하는 확실한 방법은 ‘얼마나 수정하기 쉬운가’ 이다.

      건강한 코드베이스는 생산성을 극대화하고, 고객에게 필요한 기능을 더 빠르고 저렴한 비용으로 제공하도록 해준다.

      코드를 건강하게 관리하려면 프로그래밍 팀의 현재와 이상의 차이에 항상 신경쓰면서 이상에 가까워지도록 리팩터링 해야한다.


      CHAPTER 02

      리팩터링 정의

      💡 **리팩터링이란? [명사] : 소프트웨어의 겉보기 동작은 그대로 유지한 채, 코드를 이해하고 수정하기 쉽도록 내부 구조를 변경하는 기법

      [동사] : 소프트웨어의 겉보기 동작은 그대로 유지한 채, 여러 가지 리팩터링 기법을 적용해서 소프트 웨어를 재구성하다.**


      수많은 사람이 코드를 정리하는 작업을 모조리 ‘리팩터링’ 이라고 표현하고 있는데, 앞에서 제시한 정의를 따르면 특정한 바식에 따라 코드를 정리하는 것만이 리팩터링이다.

      리팩터링 하는 동안에는 코드가 항상 정상 작동하기 때문에 전체 작업이 끝나지 않았더라도 언제든 멈출 수 있다.

      혹여 누군가가 리팩터링하다가 코드가 깨져서 며칠이나 고생했다. 라는 것은 리팩터링 한것이 아니다.

      코드베이스를 정리하거나 구조를 바꾸는 모든 작업을 재구성이라는 포괄적인 용어로 표현하고 리팩터링은 재구성 중 특수한 한 형태로 본다. 리팩터링하기 전과 후의 코드가 똑같이 동작해야 한다.

      리팩터링은 성능 최적화와 비슷하다. 둘다 코드를 변경하지만 프로그램의 전반적인 기능은 그대로 유지한다. 단지 목적이 다를 뿐이다.


      두 개의 모자

      개발을 할 때의 목적이 기능 추가 인지 리팩터링인지를 명확히 구분해서 작업한다. 켄트 벡은 이를 두개의 모자에 비유했다.

      기능을 추가할 때에는 ‘기능 추가’ 모자를 쓴 다음 기존 코드는 절대 건드리지 않고 새 기능을 추가하기만 한다.

      리팩터링을 할 때에는 ‘리팩터링’ 모자를 쓴 다음 기능추가는 절대로 하지 않기로 다짐한 뒤 오로지 코드 재구성에만 전념한다.


      리팩터링 하는 이유

      리팩터링이 소프트웨어의 모든 문제점을 해결하는 만병 통치약은 아니지만 코드를 건강한 상태로 유지하는데 도와주는 약임은 분명하다.

      • 리팩터링하면 소프트 웨어 설계가 좋아진다.

        리팩터링 하지 않으면 소프트웨어 내부 설계가 썩기 쉽다. 또한 단기 목표만을 위해 코드를 수정하다보면 기반 구조가 무너지기 쉽다.

        하지만 규칙적인 리팩터링은 코드의 구조를 지탱해 줄 것이다.

        이 과정에서 중복 코드 제거는 설계 개선 작업의 중요한 한축을 차지한다. 코드량을 줄인다고 시스템이 빨라지는 것은 아니지만 수정하는데 드는 노력은 크게 달라진다.

        → 중복 코드를 제거하면 코든 코드가 언제나 고유한 일을 수행함을 보장할 수 있다.

      • 리팩터링하면 소프트웨어를 이해하기 쉬워진다.

        컴퓨터에게 시키려는 일과 이를 표현한 코드의 차이를 최대한 줄여야 한다. 프로그래밍은 결국 내가 원하는 바를 정확히 표현하는 일이다.

        프로그램을 동작시키는 데만 신경 쓰다 보면 나중에 그 코드를 다룰 개발자를 배려하지 못한다는데 있다. 코드의 목적이 더 잘 드러나게, 내 의도를 더 명확하게 전달할수 있도록 개선한다.

        사실 다른사람을 위해서가 아니라 리팩토링을 할 사람이 나일 가능성이 많기 때문에 나를 위해서 더더욱 리팩토링이 중요하다.

      • 리팩터링하면 버그를 쉽게 찾을 수 있다.

        코드를 이해하기 쉽다는 말은 버그를 찾기 쉽다는 말이기도 하다. 리팩터링 하면 코드가 하는 일을 깊이 파악하게 되면서 새로 깨달은 것을 곧바로 코드에 반영하게 된다.

        프로그램의 구조를 명확하게 다듬으려면 버그를 지나치려야 지나칠 수 없을 정도까지 명확해진다. 리팩터링은 견고한 코드르 작성하는데 무척 효과적이다.

      • 리팩터링하면 프로그래밍 속도를 높일 수 있다.

        리팩터링하면 코드 개발 속도를 높일 수 있다. 내부 설계와 가독성이 개선되고 버그가 줄어든다는 점은 모두 품질 향상에 직결된다.

        하지만 리팩터링하는데 시간이 들다보니 전체 개발 속도가 떨어질까봐 걱정할 수 있다.

        기존 작성 중인 프로그램에 기능을 추가할 때 나쁜 설계로 시작했으면 시간이 오래걸리는 경우를 볼 수 있다. 기존에 작성한 코드를 얼마나 활용할 수 있는지에 따라 달라진다.

        소프트웨어의 내부 품질이 대표적인 원인이라고 볼 수 있다. 모듈화가 잘 되어 있으면 전체 코드베이스 중 작은 일부만 이해하면 된다. 내부 품질이 뛰어난 코드 베이슨느 새 기능 구축을 돕는 견고한 토대가 된다.


      언제 리팩터링해야 할까?

      돈 로버츠가 제시한 가이드이다.

      💡 3의 법칙

      • 처음에는 그냥 한다.

      • 비슷한 일을 두 번째로 하게 되면, 일단 계속 진행한다.

      • 비슷한 일을 세번 째 하게 되면 리팩터링한다.

      리팩터링 하기 가장 좋은 시점은 코드베이스에 기능을 새로 추가하기 직전이다. 그렇기에 기능을 쉽게 추가하게 만들어야 한다.

      준비를 위한 리팩터링으로 상황을 개선해놓으면 버그가 수정된 상태가 오래 지속될 가능성을 높이는 동시에, 같은 곳에서 다른 버그가 발생할 가능성을 줄일 수도 있다.

      코드를 수정하려면 먼저 그 코드가 하는 일을 파악해야한다. 내가 이해한 것을 코드에 반영해두면 더 오래 보존할 수 있을 뿐만 아니라 동료들도 알 수 있다.

      이해를 위한 리팩터링을 의미 없이 코드를 만지작거리는 것이라고 무시하는 이들은 복잡한 코드 아래 숨어 있는 다양한 기회를 결코 발견할 수 없다.

      그래서 코드를 이해하기 쉽게 만들어야 한다.

      코드를 파악하는 중에 일을 비효율적으로 처리하는 모습을 발견하게 될 때가 있다.

      쓰레기가 나뒹구는 것을 방치해서 나중에 일을 방해하도록 내버려 두는 것이 좋지 않은 것처럼 간단히 수정할 수 있는 것은 즉시 고치는 것이 좋다.

      💡 보기 싫은 코드를 발견하면 리팩터링하자. 그런데 잘 작성된 코드 역시 수많은 리팩터링을 거쳐야 한다.

      코드리뷰를 정기적으로 수행하며 리팩터링에 활용할 수 도 있다.

      개발팀 전체에 지식을 전파하는 데에도 좋고 더 많은 사람이 이해하는데도 도움이 된다.

      리팩터링 자체도 다른 이의 코드를 리뷰하는데도 도움이 되고 절대 떠올릴 수 없었던 높은 차원의 아이디어가 나올 수 도 있다.

      작성자와 나란히 앉아서 코드를 훑어가면서 리팩터링하는 것을 작성자가 가장 좋은 경험이였다고 한다. 이것이 바로 짝프로그래밍이다.

      리팩터링을 하지 말아야 할 떄도 있다. 내부 동작을 이해해야 할 시점에 리팩터링을 해야 효과를 제대로 볼 수 있다.

      리팩터링을 할지 새로 작성할지를 잘 결정하려면 뛰어난 판단련과 경험이 뒷받침돼야 한다.


      리팩터링 시 고려할 문제

      리팩터링의 궁극적인 목적은 개발 속도를 높이는 데 있다. 그렇더라도 상황에 맞게 조율해야 한다.

      대부분은 리팩터링을 자주 하도록 노력해야하지만 건강한 코드의 위력을 충분히 경험해보지 않았으면 생산성 차이를 체감하기 어렵다.

      하지만 가장 위험한 오류는 리팩터링을 클린코드나 바람직한 엔지니어링 습관 처럼 도덕적인 이유로 정당화하는 것이다.

      리팩터링의 본질은 코드베이스를 예쁘게 꾸미는 데 있지 않다. 오로지 경제적인 이유로 하는 것이다. 기능 추가 시간을 줄이고, 버그 수정 시간을 줄여준다.

      코드소유권이 나눠져 있으면 리팩터링에 방해가 된다. 코드의 소유권을 팀에 두는 것이 가장 선호하는 방법 중 하나인데 이렇게 해야 팀원이라면 누구나 팀이 소유한 코드를 수정할 수 있다.

      가장 많이 볼 수 있는 팀 단위 방식은 브랜치를 맡아서 작업하다가 마스터 브랜치에 통합해서 다른 팀원과 공유하는 것이다.

      브랜치에도 단점이 있는데 독립 브랜치로 작업하는 기간이 길어질수록 작업 결과를 마스터로 통합하기가 어려워진다.

      그래서 많은 이들이 개인 브랜치를 수시로 리베이스 하거나 머지 한다.

      이 떄문에 긴으별 브랜치의 통합 주기를 2~3일 단위로 짧게 관리해야 한다고 주장하는 사람이 많다. 이방식을 지속적 통합(CI), 트렁크 기반 개발(TBD) 라고 한다.

      • CI 는 모든 팀원이 하루에 최소 한 번은 마스터와 통합한다. 하지만 마스터를 건강하게 유지해야하고 거대한 기능을 잘게 쪼개는 법을 배워야 한다.

        또한 각 기능을 끌 수 있는 기능 토글을 적용하여 완료되지 않은 기능이 시스템 전체를 망치지 않도록 해야한다.

      리팩터링과 궁합이 잘맞는 CI 와 리팩토링을 합쳐서 XP(익스트림 프로그래밍) 이다.

      리팩터링하다가 실수로 동작이 깨지게 된다면 핵심은 오류를 빨리 잡는데에 있다. 코드의 다양한 측면을 검사하는 테스트 스위트가 필요하다.

      자가 테스트 코드는 리팩터링을 할 수 있게 해줄 뿐만 아니라 새로운 기능 추가도 훨씬 안전하게 진행할 수 있도록 도와준다. 버그가 생길 위험이 아주 크다는 불안감 또한 해소해준다.

      프로그래밍할 때는 물려받는 것이 좋지 않다. 물려받은 레거시 코드는 대체적으로 복잡하고 제대로 갖춰지지 않은 것이 많긴 때문이다.

      레거시 시스템을 파악할 때에는 리팩터링이 굉장히 도움된다. 만약 문제가 있다면 테스트를 보강하는 방법이 정답입니다.

      데이터베이스 리팩터링은 프로덕션 환경에 여러 단계로 나눠서 릴리스 하는 것이 대체로 좋다는 점에서 다른 리팩터링과 다르다.


      리팩터링, 아키텍쳐, 애그니(YAGNI)

      리팩터링은 소프트웨어 아키텍처를 바라보는 관점을 완전히 바꿔놓았다. 리팩터링으로 기존 코드의 설계를 개선할 수 있다.

      하지만 레거시 코드는 변경하기 어려울 떄가 많다.

      리팩터링이 아키텍처에 미치는 실질적인 효과는 요구사항 변화게 자연스럽게 대응하도록 코드베이스를 잘 설계해준다는 데 있다.

      코딩 전에 아키텍쳐를 확정지을 때의 문제는 소프트웨어 요규사항을 사전에 모두 파악해야 한다는 것이고 사실상 실현할 수 없는 목표이다.

      이를 해결하는 한가지 방법은 향후 변경에 유연하게 대처 할 수 있는 유연성 메커니즘을 소프트웨어에 심어두는 것이다.

      다양한 예상 시나리오에 대응하기 위한 매개변수들을 추가한다. 이런 매개변수가 바로 유연성 매커니즘이다.

      하지만 이것 또한 모든 상황을 고려하고자 하면 오히려 변화에 대응하는 능력을 떨어뜨릴 때가 대부분이다.

      리팩터링을 잘 활용한다면 다르게 접근할 수 있다. 앞으로 보다는 그저 현재까지 파악한 요구사항만을 해결하는 소프트웨어를 구축한다.

      매개변수를 추가해야 할 시점이 오면 간단한 리팩터링 기법인 함수 매개변수화하기로 해결한다.

      이런식으로 설계하는 방식을 간결한 설계, 점진적 설계, YAGNI ( you aren’t going to need it) 이라고 한다.


      리팩터링과 소프트웨어 개발 프로세스

      팀이 따르는 실천법에 따라 리팩터링의 효과가 크게 달라진다. XP 가 도입되어 실제로 리팩터링이 퍼지기 시작한 것이다.

      최초의 애자일 소프트웨어 방법론 중 하나로 등장한 XP 의 두드러진 특징은 지속적 통합, 자가테스트 코드, 리팩터링 등의 개성이 강하면서 상호 의존하는 기법들을 하나로 묶은 프로세스라는 점이다.

      하지만 현재 애자일을 내세운 프로젝트 중에는 이름만 애자일인 경우가 대부분이다.

      리팩터링의 첫번째 토대는 자가 테스트 코드다. 발생한 오류를 걸러내는 테스트를 자동으로 수행할 수 있어야 한다.

      팀으로 개발하면서 리팩터링을 하려면 각 팀원이 다른사람에게 방해가 되지 않고 언제든지 리팩터링 할 수 있어야 한다. 이래서 지속적 통합을 권장한다.

      세 실천법을 사용한다면 YAGNI 설계방식으로 개발을 진행할 수 있고 리팩터링을 더욱 쉽게 할 수 있다. 수많은 유연성 매커니즘을 갖춘 시스템보다는 단순한 시스템이 변경하기가 훨씬 쉽다.


      리팩터링과 성능

      직관적인 설계 VS 성능 은 중요한 주제이다. 리팩터링 하면 소프트웨어가 느려질 수도 있는 건 사실이다. 하지만 그와 동시에 성능을 튜닝하기는 더 쉬워진다.

      빠른 소프트웨어를 작성하는 방법 세가지가 있다.

      그 중 가장 엄격한 방법은 시간예산분배 방식으로, 하드 리얼타임 시스템에서 많이 사용한다.

      이 방식은 설계를 여러 컴포넌트로 나눠서 컴포넌트마다 자원 예산을 할당한다.

      두번째 방법은 끊임없이 관심을 기울이는 것이다. 직관적이어서 흔히 사용하지만 실제 효과는 번번치 않다.

      세번째 방법은 프로파일러로 프로그램을 분석하여 시간과 공간을 많이 잡아먹는 지점을 알아내고 고르게 그 부분들을 개선한다.

      이렇게 하면 성능에 큰 영향을 주는 부분만 집중해서 최적화하기 때문에 적은 노력으로 훨씬 큰 효과를 볼 수 있다.

      프로그램을 잘 리팩터링해두면 이런 식의 최적화에 두가 지 면에서 도움이 된다.

      1. 성능 튜닝에 투입할 시간을 벌 수 있다.

      2. 리팩터링이 잘 되어 있는 프로그램은 성능을 더 세밀하게 분석할 수 있다.


      리팩터링의 유래

      리팩터링의 유래는 정확히 찾을 수 없지만 실력있는 프로그래머들은 항상 자신의 코드를 정리하는데 어느 정도의 시간을 할애해왔따.

      리팩터링이 중요함을 깨달은 워드 커닝햄과 켄트 벡은 1980년대부터 스몰토크를 활용해 개발해왔다.

      스몰토크는 기능이 풍부한 소프트웨어를 빠르게 작성할 수 있는 굉장히 역동적인 환경인데, 당시 개발 환경 중에서 리팩터링을 활용해보기에 특히 좋았다고 한다.

      두 사람의 아이디어가 스몰토크 커뮤니티에 큰 반향을 일으키면서 리팩터링이란 개념이 중요한 요소로 자리 잡았다.

      랄프는 이 일을 기반으로 효율적이고 유연한 프레임워크를 개발하는데 리팩터링이 어떻게 기여하는지 연구했다.

      존 브랜트와 돈 로버츠는 빌이 제기한 리팩터링 도구 아이디어를 발전시켜서 스몰토크 환경을 위한 최초의 리팩터링 도구인 <리팩터링 브라우저> 를 개발 했다.

      다행히 리팩터링이란 개념을 업계에서 잘 받아들여졌지만 대중화되면서 코드를 재구성하는 모든 작업을 가리키는 느슨한 의미로 사용하는 사람이 많아졌다.



      우은규님 작성 : 출처 - Ch. 1~3

      Ch1 리팩터링: 첫 번째 예시

      항상 수정 시 테스트코드가 필요하다.

      한 번에 너무 많은 수정이 필요하지 않다.

      조금씩 수정하되 피드백 주기를 짧게 가져가자.

      그러면 중간에 실수하더라도 버그를 쉽게 찾을 수 있다.

      • 깃, 머큐리얼처럼 커밋을 지원하는 버전 관리 시스템을 사용하자

      반환 변수 : result

      동적타입 변수 : 타입이 드러나는 이름

      • 뚜렷하지 않으면, a/an같은 관사 - 켄트 벡

      변수 이름은 영문법을 따르자

      1. 원본 함수를 중첩 함수로 분리

      2. 계산, 출력 코드 분리

      3. 계산 로직 다형성 표현

      리팩터링은 코드가 하는 일을 파악하는데서 시작한다.

      읽고 → 개선점을 찾고 → 리팩터링 작업 → 반영

      좋은 코드를 가늠하는 방법은 “얼마나 수정하기 쉬운가”이다.

      나중에 나올것들

      함수 추출하기 - 휘발성 있는 코드는 함수화하자

      임시 변수를 질의함수로 변경

      반복문 쪼개기

      문장 슬라이드

      변수 인라인하기


      Ch2 리팩터링 원칙

      정의

      리팩터링 : 소프트웨어의 겉보기 동작은 그대로 유지한 채, 코드를 이해하고 수정하기 쉽도록 내부 구조를 변경하는 기법

      동작을 보존하는 작은 단계들을 거쳐 코드를 수정하고 이를 순차적으로 연결해 큰 변화를 만든다

      코드베이스를 정리하거나 구조를 바꾸는 모든 작업은 “재구성”이며 “리팩터링”은 그 일부이다.

      1. 기능을 추가할 땐 기존 코드를 건드리지 않고 새 기능을 추가한다.

      2. 리팩터링할 땐 기능 추가 없이 코드 정리만 한다.

      이유

      소프트웨어 설계가 좋아진다

      • 중복 코드가 없어지면 고유한 일을 수행하고 있음을 보장할 수 있다

      • 실수없는 수정이 가능하고 이해해야할 분량이 적다

      소프트웨어를 이해하기 쉬워진다

      • 나중에 봐도 쉽게 이해할 수 있다

      • 잘 읽히는 구조로 남는다

      버그를 쉽게 찾을 수 있다

      프로그래밍 속도를 높일 수 있다

      • 난잡한 코드에 비해 새 기능을 추가할때 드는 시간이 줄어든다

      • 코드의 지구력이 좋다

      언제?

      3의 법칙

      • 처음에는 그냥 한다

      • 두 번째로 하게 되면 그냥 한다

      • 세 번째로 하게 되면 리팩터링한다

      기능을 쉽게 추가할 때

      코드를 쉽게 이해할 때

      로직이 쓸데없이 복잡할 때 - 이건 기회될 때마다 하자

      수시로 리팩터링하자

      오래 걸리는 건 나눠서 하자

      코드 리뷰에도 도움이 된다

      내부동작을 이해할 필요가 없는 코드라면 리팩터링을 하지 말자

      고려점

      새 기능 개발 속도 저하 - 일의 효율이 떨아지지 않게 하자

      코드 소유권 - 쓰기 권한이 너무 세분화되면 리팩터링이 어렵다

      브랜치 - 자주 통합되어야 관리가 쉽다

      테스팅 - 리팩터링하면 동작이 깨지지 않아야 한다

      레거시 코드 - 물려받은 코드가 있으면 테스트부터

      데이터베이스 - 디비도 리팩터링 할 수 있다

      리팩터링, 아키텍처, 애그니

      유연성 매커니즘 : 변경에 의해 리팩터링이 어려워질 때의 매커니즘

      • YAGNI(You aren’t going to need it)

      • 아키텍처를 전혀 고려하지 않는게 아니라 적당한 균형점을 찾는 것

      소프트웨어 개발 프로세스

      익스트림 프로그래밍(XP) 개발로 리팩터링이 두드러짐

      XP는 지속적 통합(CI), 자가 테스트 코드, 리팩터링이 한테 묶인 프로세스

      테스트 주도 개발(TDD)은 자가 테스트 코드와 리팩터링을 묶은 말

      CI가 잘 적용되면 리팩터링 결과를 빠르게 공유하고 그 결과가 일으킨 문제 파악도 쉽다

      XP는 YAGNI를 쓸 수도 있고 배포 주기를 짧게 가져가기도 한다

      성능

      리팩터링 할 때 성능이 낮아지는 방향으로 하기 쉽다.

      대신, 개발 속도가 올라갈 가능성이 높아진다.

      “아무것도 안 만드는 데도 시간이 걸린다” - 론 제프리

      리팩터링 후 성능 튜닝을 해도 좋다


      Ch3 코드에서 나는 악취

      악취가 나는 코드를 찾고 적절하게 없애기

      기이한 이름

      이름만 보고도 역할을 알 수 있는 명료한 명칭이어야 한다

      마땅한 이름이 떠오르지 않으면 근본적으로 문제가 있을 수 있다

      중복 코드

      중복 코드가 있으면 함수로 추출하여 사용하자

      문장 슬라이드를 통해 비슷한 로직을 모으고 함수로 추출 가능한지 살펴보자

      만약 완전히 같은 코드가 아니라면 공통 기능만을 함수로 추출한다

      긴 함수

      함수 내부 코드가 길다면 디버깅이 어려울 것이다.

      요즘 언어에서 함수 호출 비용이 크지 않다

      별도로 함수를 추출하여 쪼개자

      전역 데이터

      전역 데이터는 코드 어디서든 접근 가능하므로 사용이 까다롭다

      함수 스코프로 넣어두자

      가변 데이터

      변수를 캡슐화하자

      갱신 코드는 다른 코드와 분리하자

      뒤엉킨 변경

      서로 다른 맥락에서 이루어진 동작은 독립된 모듈로 분리한다

      분리 후에는 클래스 추출로 클래스 동작을 분할한다

      산탄총 수술

      변경할 부분이 코드 전반에 있다면 찾기 어렵고 지나치기 쉽다

      함수와 필드를 한 모듈로 묶어두면 좋다

      기능 편애

      모듈화할 때 코드에서 나뉜 영역 내에서는 상호작용이 많고 외에서는 적도록 하자

      코드가 데이터 근처에 있어야 한다면 옮겨주자

      데이터 뭉치

      데이터는 뭉쳐다닌다 이 경우 하나로 묶어두자

      데이터가 묶여서 특정 역할을 한다면 그것이 데이터 뭉치이다

      기본형 집착

      기본형만 집착하지 말고 클래스로 만들어 쓰자

      반복되는 switch문

      중복된 switch문은 조건절을 추가할 때마다 다른 switch문도 찾아 수정해야한다

      이런 부분을 잘 수정해주자

      반복문

      반목문을 파이프라인으로 바꾸자

      성의 없는 요소

      클래스나 메서드를 애용하되 굳이 그럴 필요가 없다면 인라인으로 바꾸자

      추측성 일반화

      나중에 필요할거라는 생각으로 냅두지 말고

      당장 쓸모없으면 눈앞에서 치우자

      임시 필드

      클래스 특정 필드 값이 채워질수도 있고 아닐수도 있다면 좋지 않다

      클래스로 추출하거나 Null로 처리해두자

      메시지 체인

      객체로 다른 객체를 얻는 체인이 이어지면 좋지 않다..

      기능을 추출해 체인 시작 부분으로 옮겨주자

      중개자

      중개자 역할만 하는 클래스라면 치워버리자

      내부자 거래

      모듈 간 데이터 거래가 많으면 결합도가 높아진다

      은밀한 데이터 교환이 이루어지는 클래스는 따로 떼어놓자

      거대한 클래스

      클래스가 너무 많은 일을 하면 필드 수가 늘어난다

      그에 따라 중복 코드 발생도 쉽다

      클래스를 잘 쪼개보자

      서로 다른 인터페이스의 대안 클래스들

      두 클래스(메서드)의 역할이 비슷할 때 인터페이스를 통합시키고 합쳐보자

      데이터 클래스

      데이터 클래스는 데이터 필드와 게터/세터 메서드로만 구성된 클래스이다

      데이터 저장 용도로만 쓰니 함부로 다뤄질 때가 있다

      변경하면 안되는 필드는 세터를 없애버리자

      필요한 동작이 엉뚱한 데 정의돼있다는 신호일 수 있다.

      클라이언트 코드를 데이터 클래스로 가져오자

      상속 포기

      부모의 상속이 필요치 않다면 서브클래스 위임으로 상속에서 벗어나자

      그러나 그다지 권하지는 않는다

      주석

      주석이 정확하게 적힌게 잘못된 코드 때문인 경우가 있다

      주석이 필요하다는 생각이 들면 주석이 필요 없는 코드를 생각해보자



      장형준님 작성 : 출처 - Ch. 1~2, 4

      • 1장. 리팩터링 : 첫 번째 예시

      • 2장. 리팩터링 원칙

        1. 리팩터링 정의

          • 리팩터링: 소프트웨어의 겉보기 동작은 그대로 유지한 채, 코드를 이해하고 수정하기 쉽도록 내부 구조를 변경하는 기법

          • 성능 최적화: 프로그램의 전반적인 기능은 그대로 유지한채 코드를 변경하는 것은 리팩터링과 같지만, 오로지 속도 개선에만 신경 쓴다는 점에서 차이가 있다.

        2. 두 개의 모자

          • 기능 추가 모자와 리팩터링 모자를 명확히 구분하여 오로지 하나의 모자만 쓸 것

        3. 리팩터링하는 이유

          • 소프트웨어 설계가 좋아진다.

            • 비슷한 일을 하는 코드가 산재해 있다면 한 부분만 바꿔도 시스템이 작동하지 않을 수 있지만, 중복 코드를 제거하면 모든 코드가 고유한 작업이 보장될 수 있다.

          • 소프트웨어를 이해하기 쉬워진다.

          • 버그를 쉽게 찾을 수 있다.

          • 프로그래밍 속도를 높일 수 있다.

            • 내부 설계와 가독성이 개선되고 버그가 줄어들어 품질 향상은 명확하지만 리팩터링에 소요되는 시간으로 인해 전체 개발 속도가 느려질 수 있다.

              하지만 새로운 기능을 추가하거나 삭제하는 과정이 발생할 수록 전체 개발 시간은 줄어든다.

        4. 언제 리팩터링해야 할까?

          • 3의 법칙

            1. 처음엔 그냥 한다.

            2. 비슷한 일을 두 번째로 하게 되더라도 일단 진행한다.

            3. 비슷한 일을 세 번째 하게 되면 리팩터링을 한다.

          • 준비를 위한 리팩터링: 기능을 새로 추가하기 직전

            • 구조만 살짝 바꿨을 때 훨씬 쉬워질 만한 부분을 찾는다.

          • 이해를 위한 리팩터링: 코드의 의도를 더 명확하게 드러내고자 할 때

          • 쓰레기 줍기 리팩터링: 쓰레기가 나뒹굴게 방치하지 않고 조금이나마 개선해두는 것이 좋다.

          • ‘보기 싫은 코드를 발견하면 리팩터링하자. 그런데 잘 작성된 코드 역시 수많은 리팩터링을 거쳐야 한다,’

          • 오개 걸리는 리팩터링: 팀 전체가 매달리기 보단 작업을 할 때마다 원하는 방향을 조금씩 개선하는 편이 효과적일 때가 많다.

          • 코드 리뷰에 리팩터링 활용하기

          • 리팩터링하지 말아야 할 때

            • 굳이 수정할 필요가 없을 때(e.x. 외부 API 다루듯 호출해서 쓰는 코드).

            • 리팩터링보다 새로 작성하는 게 쉬울 때

        5. 리팩터링 시 고려할 문제

          • 새 기능 개발 속도 저하

            • ‘리팩터링의 궁극적인 목적은 개발 속도를 높여서, 더 적은 노력으로 더 많은 가치를 창출하는 것이다.’

          • 코드 소유권

          • 브랜치

          • 테스팅

            • ‘Self-testing code’를 마련하라

          • 레거시 코드

            • 관련된 부분끼리 나눠서 하나씩 공략하라

          • 데이터베이스

        6. 리팩터링, 아키텍처, 애그니(YAGNI)

          • 코딩 전에 아키텍처를 확정지으려면 소프트웨어 요구사항을 사전에 모두 파악해야 하는 문제가 있다.

            → Flexibility mechanism을 소프트웨어에 심어둬라 : 예상 시나리오에 대응하기 위한 매개변수 추가

            • 호출하는 측에서 항상 같은 값을 넘기는 매개변수는 매개변수 목록에 넣지 않다가 추가해야 할 시점이 오면 리팩터링 기법 ‘함수 매개변수화하기’로 해결한다.

            • 이러한 설계 방식을 ‘Simple design’, ‘Incremental design’, ‘YAGNI: “you aren’t going to need it” ’ 등으로 부른다.

        7. 리팩터링과 소프트웨어 개발 프로세스

          • Test-Driven Development(TDD): Self-testing code with Refactoring

        8. 리팩터링과 성능

          • 리팩터링을 하면 소프트웨어가 느려질 수 있지만, 세 가지 방법으로 빠른 소프트웨어를 작성할 수 있다.

            1. Time budgeting 방식: 설계를 여러 컴포넌트로 나눠서 각 컴포넌트 마다 자원(시간과 공간)을 할당하는데, 전체 자원 예산을 초과할 수 없으나 서로 주고받을 순 있다.

            2. 끊임없이 관심을 기울여라

            3. 90%의 시간은 낭비: 대부분의 프로그램은 전체 코드 중 극히 일부에서 대부분의 시간을 소비하기 때문에, 프로파일러를 사용해 프로그램 분석을 한 뒤 자원을 많이 잡아먹는 지점을 알아내어 개선한다.

        9. 리팩터링의 유래

          • 정확한 유래는 찾을 수 없지만, 워드 커닝햄과 켄트 벡이 1980년대부터 스몰토크를 활용해 리팩터링을 개발해왔다.

        10. 리팩터링 자동화

          • <IntelliJ IDEA> <Eclipse>

      • 4장. 테스트 구축하기

        1. Self testing code의 가치

          • “모든 테스트를 완전히 자동화하고 그 결과까지 스스로 검사하게 만들자”

          • “테스트 스위트는 강력한 버그 검출 도구로, 버그를 찾는 데 걸리는 시간을 대폭 줄여준다”

        2. 테스트할 샘플 코드

          1. 생산 계획은 각 지역의 수요와 가격으로 구성된다.

          2. 생산자들은 각 제품을 특정 가격으로 특정 수만큼 생산할 수 있다.

          3. 사용자는 UI에서 수요, 가격, 생산자별 생산량과 비용을 조정해가며 생산 부족분과 총수익을 확인할 수 있다.

        3. 첫 번째 테스트

          1. 모카 프레임워크를 사용한다.

          2. 테스트에 필요한 데이터와 객체를 뜻하는 Fixture를 설정한다.

          3. 설정된 Fixture의 속성을 검증하기 위해 생산 부족분을 정확히 계산했는지 확인한다.

        4. 테스트 추가하기

          1. 클래스가 하는 일을 모두 살펴보고 각 기능에서 오류가 생길 수 있는 조건을 하나씩 테스트하겠다.

          2. “테스트는 위험 요인을 중심으로 작성해야 한다!”

          3. “완벽하게 만드느라 테스트를 수행하지 못하느니, 불완전한 테스트라도 작성해 실행하는 게 낫다.”

          4. ‘테스트끼리 상호작용하게 하는 공유 픽스처’는 테스트 관련 버그 중 가장 지저분한 유형이 생성되는 것이다.

        5. 픽스처 수정하기

        6. 경계 조건 검사하기

          1. “문제가 생길 가능성이 있는 경계 조건을 생각해보고 그 부분을 집중적으로 테스트하자.”

          2. “어차피 모든 버그를 잡아낼 수는 없다고 생각하여 테스트를 작성하지 않는다면 대다수의 버그를 잡을 수 있는 기회를 날리는 셈이다.”

        7. 끝나지 않은 여정

          1. “버그 리포트를 받으면 가장 먼저 그 버그를 드러내는 단위 테스트부터 작성하자.”



      송주현님 작성 : 출처 - Ch. 9

      Refactoring 2nd - 9장 데이터 조직화

      9장을 공부하며 Rust를 사용한 이유

      • 메모리 안전성/자유성

        • 스택과 힙을 넘나들며 객체를 만들 수 있습니다.

        • 쓰레기 수집기(GC)가 없기에 내가 사용중인 객체에 대한 수명을 생각할 힘을 길러줍니다.

        • 각종 생성자들을 직접 구현해야 하나, 이를 통해 얕은 복사, 깊은 복사가 일어날 상황을 명확히 구분할 수 있습니다.

      • mutable/Immutable, 참조/비참조의 엄격함

        • let mut 변수 -> Mutable, let 변수 ->Immutable으로써, 수정 가능/불가능한 변수를 엄격히 나누어 생각할 힘을 길러줍니다.

        • Rust에서는 메서드 호출시 개체를 참조할지 소유권을 이전할지 조차 매개 변수로 지정합니다.

      • 그래서 책의 자바스크립트 보다 훠얼씬 불편하지만, 그 불편함을 통해 배울 것이 많은 Rust로 구현해봤습니다.

      9.1 임시 변수 분리

      • 기본 예제

        • 다음 변수를 쪼개봅시다!

          fn print_info(height : u32, width: u32)
          {
              let mut temp = 2 *(height + width);
          
              print!(temp);
          
              temp = height * width;  
          
              print!(temp);
          }
        • Immutable(변경 불가능한, const 변수 같은 거) 변수로 나눕시다!

          fn print_info(height : u32, width: u32)
          {
              let length = 2 *(height + width); //길이 변수랑
          
              print!(length);
          
              let area = height * width; //면적 변수로
          
              print!(area);
          
              //temp라는 임시 변수를 나누었습니다! 의미가 명확해진 거 같습니다!
          }
      • 예시: 입력 매개변수의 값을 수정할 때

        • 다음 코드를 고쳐봅시다!

          fn discount(original_input_val: u32, quantity: u32) -> u32 {
          
              let mut input_value = original_input_val.clone();
          
              if input_value > 50 {
          
                 input_value = input_value - 2;
          
              }
              if quantity > 100 {
          
                 input_value = input_value - 1;
          
              }
          
               return input_value;
          }
        • 무슨 동작을 하는지 한눈에 보이기 시작했습니다!

          fn discount(input_value: u32, quantity: u32) -> u32 {
          
              let mut result = input_value.clone();
          
              if input_value > 50 {
          
                 result = result - 2;
          
              }
              if quantity > 100 {
          
                 result = result - 1;
          
              }
          
               return result;
          }

      9.2 필드 이름 바꾸기

      • 예시

        • 클래스(구조체)에 마음에 들지 않는 필드 이름이 보입니다!

          struct organization
          {
            name: String, // 맘에 들지 않으니 title로 바꾸고 싶습니다!
            country: String
          }
          
          impl organization
          {
            fn new(name: String, country: String) ->Self //생성자
            {
              Self
              {
                name: name,
                country: country
              }
            }
          
            fn get_name(&self) -> String //self -> X &self -> O : 메서드 호출시 개체가 함수 안에서 소멸하지 않도록 "참조" 해줍니다!
            {
              self.name
            }
          }
        • 일단 클래스(구조체) 내부 변수명(필드명)부터 바꾸어줍니다.

          struct organization
          {
            title: String, // 필드명부터 바꾸고 꼭!! 테스트 합니다!
            country: String
          }
          
          impl organization
          {
            fn new(name: String, country: String) ->Self //생성자
            {
              Self
              {
                title: name,
                country: country
              }
            }
          
            fn get_name(&self) -> String
            {
              self.title
            }
          }
        • 위 과정에 대한 테스트를 충분히 하고, 외부에서 호출하는 메서드를 수정하세요! 무조건 테스트! 테스트!

          struct organization
          {
            title: String, // 일단 이건 바꾸었고,
            country: String
          }
          
          impl organization
          {
            fn new(title: String, country: String) ->Self //생성자 매개변수도 수정!
            {
              Self
              {
                title: title,
                country: country
              }
            }
          
            fn get_title(&self) -> String // 매개변수, 함수(메서드) 이름 수정! 
            {
              self.title
            }
          }
          //휴 끝났다..가 아니죠 꼭!! 테스트하세요!!

      9.3 파생 변수를 질의 함수로!

      • 예시

        • 함수의 기능에 주목합시다!

          struct sale_info {
              discounted_total: u32,
              base_total: u32,
              discount: u32,
          }
          
          impl sale_info {
              fn set_discount(&mut self, number: u32) {
                let old = self.discount;        //하나의 메서드 안에서 파생 변수를 만들어 여러 변수를 반복해서 바꾸고 있습니다.
                self.discount = number;
                self.discounted_total += old - number;
              }
              fn get_discounted_total(&self) -> u32 {
                  self.discounted_total
              }     
          }
        • 그래서 내부 파생 변수를 참조해서 반환하는 get_discounted_total() 함수를 바꾸어봅시다!

          struct sale_info {
              //discounted_total: u32, //-> 파생해서 나온 변수는 제거합시다!
              discount: u32,
              base_total: u32, // 이 변수를 통해
              //Total Discount는 요청하면 그때그때 계산해서 함수로 반환합시다!
          }
          
          impl sale_info {
              fn set_discount(&mut self, number: u32) {
                self.discount = number;
              }
              fn get_discounted_total(&self) -> u32 { 
                  self.base_total - self.discount //훨씬 깔끔해 보입니다!
              }     
          }

      9.4 참조를 값(혹은 복사된 개체)으로 바꾸기!

      • 예시

        • 여기 생성 시점에서 내부 필드(변수값)가 지정되지 않은 전화번호 클래스(구조체)가 있습니다.

            struct TelephoneNumber {
              area_code: u32,
              number: u32,
            }
            struct Person {
               telephone_number: Rc<TelephoneNumber>, 
               //Rc<타입> : Rust에서 참조 횟수라는 것으로 수명을 정하는 스마트 포인터, Rc입니다.
               //무섭게 생겼으나, 원리는 간단합니다. 힙 할당 후, 참조하면 횟수가 오르고 참조하지 않으면 횟수가 줄어들다가
               //0이 되면, 자기 자신을 할당 해제 합니다.
               // 이것은 Swift라는 언어와 동일한 방식입니다.
            }
          
            impl Person {
             fn new() -> Self {
                Self {
                  telephone_number: Rc::new(TelephoneNumber {//본론으로 돌아와서 
                  area_code: 0,  //생성자에서 불필요하게 객체가 초기화 되는 모습을 여기서 볼 수 있습니다!
                  number: 0 
                  }),
                    }
              }
              fn set_office_area_code(&mut self, area_code: u32) {
                let ptr = Rc::get_mut(&mut self.telephone_number).unwrap(); //불필요한 참조가 여기도 있고,(강한 참조 + 1)
                ptr.area_code = area_code;
              }
              fn get_office_area_code(&self) -> u32 {
                self.telephone_number.area_code
              }
              fn set_office_number(&mut self, number: u32) {
                let ptr = Rc::get_mut(&mut self.telephone_number).unwrap(); //여기도 있습니다.(강한 참조 + 1)
                ptr.number = number;
              }
              fn get_office_number(&self) -> u32 {
                self.telephone_number.number
              }
            }
        • 그래서 참조를 제거하고 복사로 깔끔하게 바꾸어 보려고 합니다!

           struct TelephoneNumber {
            area_code: u32,
            number: u32,
           }
           impl TelephoneNumber {
            fn new(area_code: u32, number: u32) -> Box<Self> {
             Box::new(Self {
              area_code: area_code,
              number: number, //Box<타입>: Rust를 이루는 모든 것이라할 수 있는 "소유권" 기반 힙 스마트 포인터 입니다.
                              //여기서 설명하려면 머리 아프니 
                              //자바,JS에서 new TelephoneNumber()와 같이 힙에 객체를 할당해서 반환하는 생성자라고 생각해주세요!
            })
            }
           }
           struct Person {
             telephone_number: Box<TelephoneNumber>,
            }
          
            impl Person {
             fn new(area_code: u32, number: u32) -> Self {
              Self {
               telephone_number: TelephoneNumber::new(area_code, number),
              }
             }
             fn set_office_area_code(&mut self, area_code: u32) {
               self.telephone_number = TelephoneNumber::new(area_code, self.telephone_number.number); //new TelephoneNumber(번호, 지역번호); 와 같습니다.
             }
             fn get_office_area_code(&self) -> u32 {
               self.telephone_number.area_code
             }
             fn set_office_number(&mut self, number: u32) {
               self.telephone_number = TelephoneNumber::new(self.telephone_number.area_code, number); //new TelephoneNumber(번호, 지역번호);와 같습니다.
              }
             fn get_office_number(&self) -> u32 {
              self.telephone_number.number
             }
            }

      9.5 값(혹은 복사된 개체)를 참조로 바꾸기!

      • 예시

        • 불필요한 복사에 대해 거대한 하나의 객체로 부터 참조 받아오는 방법이 책에 소개 되어있습니다.

          struct Customer {
             id: String,
          }
          
          impl Clone for Customer {
           fn clone(&self) -> Self { //Customer Class의 복사생성자
            Self {
               id: self.id.clone(),
            }
           }
          }
          impl Customer {
           fn new(id: String) -> Self {
             Self { id: id }
            }
          }
          struct Order {
             number: u32,
             customer: Box<Customer>,
          }
          
          impl Order {
            fn new(number: u32, id: String) -> Self {
               Self {
                  customer: Box::new(Customer::new(id)), //여기서 개체 복사(복사 생성)가 발생해, 참조가 아닌 새로운 값(객체)으로 할당하고 있습니다.
                  number: number,
               }
            }
          }
        • 이러한 경우에는 Map자료형으로 된 거대한 하나(Singleton)의 객체 저장소를 만들어 사용하는 방식을 저자가 추천하고 있습니다!

           use std::collections::HashMap;
           use std::rc::*;
           struct Customer {
             id: String,
           }
           impl Clone for Customer {
             fn clone(&self) -> Self {
              Self {
               id: self.id.clone(),
              }
             }
           }
           impl Customer {
            fn new(id: String) -> Self {
             Self { id: id }
               }
           }
           static mut REPOSITORY: Option<HashMap<String, Rc<Customer>>> = None; //하나의 큰(Singleton) 저장소 객체를 전역적으로 구현하기 위함.
           unsafe fn init_repo() {
                REPOSITORY = Some(HashMap::new())
           }
           unsafe fn register_customer(id: String) -> Rc<Customer> {
              if (REPOSITORY.is_none()) {
                 init_repo();
              };
              if REPOSITORY.as_mut().unwrap().get(&id.clone()).is_none() {  //책과 살짝 다르지만 동작은 같습니다.
                REPOSITORY
               .as_mut()
               .unwrap()
               .insert(id.clone(), Rc::new(Customer::new(id.clone())));
              }
              return Rc::clone(REPOSITORY.as_mut().unwrap().get(&id.clone()).unwrap()); 
              //참조를 반환하기 위해 참조 기반 스마트 포인터인 RC를 사용했습니다.
           }
           struct Order {
             number: u32,
             customer: Rc<Customer>,
           }
           impl Order {
             fn new(number: u32, id: String) -> Self {
               Self {
                    customer: unsafe {register_customer(id)},  
                    //하나의 큰 저장소의 맵 자료구조에다가 저장해놓고, 그 안에서의 위치(참조)받아서 저장
                    number: number,
                  }
               }
           }


      후기

      여러 프로그램이 얽힌 큰 개발보다 간단간단한 문제 풀이를 위한 코드를 많이 작성했던 저도 예전에 짰던 코드를 다시 사용하려고 할 때 깨끗하지 못해서 거의 처음부터 다시 작성했던 기억들이 있습니다.

      의미를 알 수 없는 요상한 변수/함수 명, 분명 무언가 바뀌었는데 어디가 바뀐지 알 수 없는 중복 코드들, 가끔은 작동하고 가끔은 버그가 나는 코드, 그리고 너무나 압축된 나머지 원리를 파악할 수 없는 메커니즘.

      리팩터링 책을 읽은 후에는 이런 코드들을 어떻게 수정하고 다시 사용할 수 있을지 생각할 수 있게 되었습니다.

      모든 코드를 한번에 리팩터링할 필요가 없고, 하나하나씩 바꿔나간다면 결국 나중에 사용할 때도 쉽게 이해하고 응용할 수 있는 건강한 코드가 될 것 같습니다.

      리팩터링에도 시간이 들지만 미래의 내가 개발을 할 때 개발 효율을 높일 수 있고 설계를 향상시켜준다는 점에서도 도움이 된다고 생각했습니다.

      더 나은 개발자가 될 수 있는 한 걸음을 가져다 준 이 책과 리팩터링을 읽을 기회를 주신 데보션에 감사합니다.

      댓글 0

      DEVOTEE를 활성화 시키면
      지금 작성한 댓글에 AI가 댓글을 달아줍니다.

      ekwoo08 님의 최신 블로그

      더보기
      동영상 기고하기