[리팩터링] 리팩터링 직접 해보기
요약
- 프로그램이 새로운 기능을 추가하기에 편한 구조가 아니라면, 먼저 기능을 추가하기 쉬운 형태로 리팩터링하고 나서 원하는 기능을 추가한다.
- 리팩터링하기 전에 제대로 된 테스트부터 마련한다. 테스트는 반드시 자가진단하도록 만든다.
- 코드를 분석하지 않아도, 코드 스스로가 자신이 하는 일이 무엇인지 이야기할 수 있도록 작성해라. 코드를 분석해서 얻은 정보는 휘발성이 높기로 악명 높은 저장 장치인 머릿속에 기억된다.
- 컴퓨터가 이해하는 코드는 바보도 작성할 수 있다.
사람이 이해하도록 작성하는 프로그래머가 진정한 실력자다. - 지역 변수는 추출을 까다롭게 만든다. 지역 변수를 쿼리 메소드로 대체하자.
- 리팩터링으로 인한 성능 문제는 '특별한 경우가 아니라면 일단 무시하라'!
- 나중에 깔끔해진 코드로 쉽게 개선하라.
- 리팩토링 과정 도중에 발생하는 중복은 허용하라.
- 일단은 만들고, 나중에 제거하라 - 테스트 주도 개발 -
- 좋은 코드를 가늠하는 확실한 방법은 "얼마나 수정하기 쉬운가"이다.
리팩터링은 실제로 해보기 전까진 추상적인 내용으로밖에 들리지 않는다.
그렇다고 공부를 안하자니, 뭐가 예쁜 코드이고, 뭐가 읽기 힘든 코드인지 파악하기 어렵고,
내가 읽기 쉬운게 과연 모두가 읽기 쉬운 코드인가? 라는 질문을 수시로 던져야 하기에 여간 피곤한 기준이 아닐 수 없다.
"모두가 공통으로 사용하는 예쁜 코드와 리팩터링에 대한 기준이 있으면 참 좋겠다"라는 생각은 항상 나를 따라다니던 그림자였다.
그래서 마틴 파울러의 리팩터링 교재를 읽으며, 그 기준을 잡아보고자 한다.
자, 시작해보자!
요구사항은 아래와 같다.
다양한 연극을 외주로 받아서 공연하는 극단이 있다고 생각해보자.
공연 요청이 들어오면 연극의 장르와 관객 규모를 기초로 비용을 책정한다.
현재 이 극단은 두 가지 장르, 비극(tragedy)와 희극(comedy)만 공얀한다.
그리고 공연료와 별개로 포인트(volume credit)를 지급해서 다음 번 의뢰 시 공연료를 할인받을 수도 있다. 일종의 충성도 프로그램인 셈이다.
극단은 공연할 연극 정보를 다음과 같이 간단한 JSON 파일에 저장한다.
공연료 청구서에 들어갈 데이터도 다음과 같이 JSON 파일로 표현한다.
공연료 청구서를 출력하는 코드는 다음과 같이 간단히 함수로 구현했다.
이를 실행하면 다음과 같은 결과가 나온다.
예시 프로그램을 본 소감
현재는 쓸만해보인다.
근데 코드가 커지면 불편해질 듯 하다.
프로그램이 잘 작동하는 상황에서 그저 코드가 '지저분하다'라는 이유로 불평하는 것은 프로그램의 구조를 너무 미적인 기준으로만 판단하는 건 아닐까?
하지만 코드를 수정하려면 사람이 개입되고, 사람은 코드의 미적 상태에 민감하다.
무엇을 수정할지 찾기 어렵다면 실수를 저질러서 버그가 생길 가능성도 높아진다.
프로그램이 새로운 기능을 추가하기에 편한 구조가 아니라면, 먼저 기능을 추가하기 쉬운 형태로 리팩터링하고 나서 원하는 기능을 추가한다.
가정 1: 청구 내역을 HTML로 출력하는 기능을 추가해야 한다면?
- statement() 함수 복사해서 붙여넣고, statementWithHTML() 메소드 추가?
- 최악!!!
- 청구서 생성 로직은 중복이다.
- 일단 이렇게 진행했다고 가정해보자.가정 2: 배우들이 더 많은 장르를 연기하고 싶어한다면?
- 희극과 비극 외에도, 사극, 전원극, 전원 희극, 역사 전원극, 등등 더 많은 장르를 연기하고 싶어한다면?
- 이 변경은 공연료와 적립 포인트 계산법에 영향을 줄 것이다.
- 그러면 수많은 중복 로직들이 다 동일한 절차를 따르는지 검증하는 작업이 필요해진다.
- 마틴 파울러는 이렇게 말했다.
- 경험 많은 개발자로서 내가 장담하건대, 어떤 방식으로 정하든 반드시 6개월 안에 다시 변경하게 될 것이다.
- 새로운 요구사항은 수색 대원처럼 한두 명씩이 아니라, 한 부대씩 몰려오기 마련이다.
잘 작동하고 나중에 변경할 일이 "절대" 없다면, 코드를 현재 상태로 나눠도 아무런 문제가 없다.
하지만 그러다 다른 사람이 읽고 이해해야 할 일이 생겼는데 로직을 파악하기 어렵다면 뭔가 대책을 마련해야 한다.
리팩터링의 첫 단계
리팩터링의 첫 단계는 항상 똑같다.
바로 테스트 코드 작성이다.
리팩터링을 수행하다가 잘못된 수정을 했을 때 검사해주는 최고의 도구는 테스트 코드 뿐이다.
리팩터링하기 전에 제대로 된 테스트부터 마련한다.
테스트는 반드시 자가진단하도록 만든다.
테스트를 작성하는 데 시간이 좀 걸리고, 데이터를 구비하는것도 힘들겠지만,
신경 써서 만들어두면 디버깅 시간이 줄어서 전체 작업 시간은 오히려 단축된다.
리팩터링에서 테스트의 역할이 굉장히 중요하기 때문에 4장 전체를 테스트에 할애했다.
statement() 함수 쪼개기
statement()처럼 긴 함수를 리팩터링할 때는 먼저 전체 동작을 각각의 부분으로 나눌 수 있는 지점을 찾는다.
그럼 가장 먼저 switch문이 눈에 들어올 것이다.
코드를 분석하지 않아도, 코드 스스로가 자신이 하는 일이 무엇인지 이야기할 수 있도록 작성해라.
코드를 분석해서 얻은 정보는 휘발성이 높기로 악명 높은 저장 장치인 머릿속에 기억된다.
그럼 아래 코드를 별도의 함수로 빼내보자.
이런 절차를 "함수 추출하기"라고 한다.
추후 리팩토링 사전을 만들어 나갈 때 언급할 것이다.
함수 추출하기를 적용하는 순서는 다음과 같다.
- 별도 함수로 빼냈을 때 유효범위를 벗어나는 변수, 즉 새 함수에서는 곧바로 사용할 수 없는 변수가 있는지 확인한다.
- 이번 예에서는 perf.play, thisAmount가 여기 속한다.
- 이 변수들이 함수 내에서 값이 변경되는지/변경되지 않는지 확인한다.
- 함수 안에서 변경되지 않는 변수는 파라미터로 전달시킨다.
- 함수 안에서 변경되는 값은 "적절한 처리"를 수행해야 한다.
- 이번 예에선 바뀌는 값을 반환하도록 처리했다.
- 또한 이 변수를 초기화하는 코드도 추출한 함수에 넣었다.
결과는 아래와 같다.
이제 statement()에서는 thisAmount값을 채울 때 방금 추출한 amountFor() 함수를 호출한다.
이렇게 수정하고 나면 곧바로 컴파일하고 테스트해서 실수한 게 없는지 확인한다.
아무리 간단한 수정이라도 리팩터링 후에는 항상 테스트하는 습관을 들이는 것이 바람직하다.
리팩터링은 프로그램 수정을 작은 단계로 나눠 진행한다. 그래서 중간에 실수하더라도 버그를 쉽게 찾을 수 있다.
이렇게 하나의 변경사항 마다, 커밋을 1회씩 넣는 것이 좋다.
그래야 중간에 문제가 생기더라도 이전의 정상 상태로 쉽게 돌아갈 수 있다.
함수 추출하기는 흔히 IDE에서 자동으로 수행해준다.
IntelliJ에선 ctrl+alt+M(command+opt+M), vscode에서는 ctrl+.(command+.) 후 extract method 를 수행하면 된다.
이제 추출된 함수 코드를 자세히 들여다보며, 지금보다 명확하게 표현할 수 있는 간단한 방법은 없는지 검토하자.
가장 먼저 변수의 이름을 더 명확하게 바꿔보자.
가령 thisAmount를 result로 변경할 수 있다.
나는 함수의 반환 값에는 항상 result라는 이름을 쓴다.
그러면 그 변수의 역할을 쉽게 알 수 있다.
이번에도 마찬가지로 컴파일하고, 테스트하고, 커밋하자.
다른 변수 이름은 모호한게 없는지 확인해본다.
다음은 매개변수 perf를 aPerformance로 리팩터링해보자.
이번에도 내(마틴 파울러의) 코딩 스타일에 따라 처리했다.
자바스크립트와 같은 동적 타입 언어를 사용할 때는 타입이 드러나게 작성하면 도움된다.
그래서 나는 매개변수 이름에 접두어로 타입 이름을 적는데. 지금처럼 매개변수의 역할이 뚜렷하지 않을 때는 부정관사(a, an)을 붙인다.
이 방식은 켄트 백에게 배웠는데 쓰면 쓸수록 정말 유용한 것 같다.
컴퓨터가 이해하는 코드는 바보도 작성할 수 있다.
사람이 이해하도록 작성하는 프로그래머가 진정한 실력자다.
다음으로 play 매개변수의 이름을 바꿀 차례이다. 그런데 이 변수는 좀 다르게 처리해야 한다.
play 변수 제거하기
amountFor()의 매개변수를 살펴보면서 이 값들이 어디서 오는지 알아봤다.
곰곰이 생각해보면, play는 aPerformance에서 들어온다.
이러한 변수는 amountFor()안에서 다시 꺼내는 것이 좋다.
이런 임시 변수들 때문에 로컬 범위에 존재하는 이름이 늘어나서 추출 작업이 복잡해지기 때문이다.
이를 해결해주는 리팩터링으로는 "임시 변수를 질의 함수로 바꾸기"(7.4절)가 있다.
먼저 대입문의 우변을 함수로 추출한다.
컴파일-테스트-커밋한 다음 변수 인라인하기(6.4절)를 적용한다.
변수를 쓰는게 아닌 질의 함수를 호출해 사용했다.
다시 컴파일-테스트-커밋을 수행하고, 이번엔 함수 선언 바꾸기를 수행하자.
이는 두 단계로 진행된다.
- play 대신 playFor()를 사용하도록 변경한다.
- 파라미터에 있는 play 매개변수를 제거한다.
이를 1번부터 수행해보자.
컴파일 테스트 커밋하고, 이제 2번을 수행하자.
이러면 성능에 문제가 없나? 싶겠지만, 다음과 같은 이유로 해당 작업을 수행한다.
- 이렇게 변경해도 성능에 큰 영향은 없다. (빅오 표기법을 생각해보자.)
- 설사 심각하게 느려지더라도 제대로 리팩터링된 코드베이스는 그렇지 않은 코드보다 성능을 개선하기가 훨씬 수월하다.
지역 변수를 제거해서 얻는 가장 큰 장점은 추출 작업이 훨씬 쉬워진다는 것이다.
유효 범위를 신경써야 할 대상이 줄어들기 때문이다.
실제로 마틴 파울러는 추출 작업 전에 거의 항상 지역 변수부터 제거한다고 한다.
지역 변수는 추출을 까다롭게 만든다.
지역 변수를 쿼리 메소드로 대체하자.
이제 이 amountFor을 호출하는 함수로 되돌아가보자.
그 값이 다시 바뀌지는 않는 것을 확인할 수 있다.
따라서 변수 인라인하기(6.4절)을 적용한다.
적립 포인트 계산 코드 추출하기
지금까지 구현한 코드는 아래와 같다.
앞에서 play 변수를 제거한 결과 로컬 유효범위의 변수가 하나 줄어서 적립 포인트 계산 부분을 추출하기가 훨씬 쉬워졌다.
아직 추출해야 할 변수가 2개 남아있다.
- volumeCredits
- totalAmount
우선 volumeCredits를 제거해보자.
volumeCredits는 반복문을 돌 때마다 값을 누적하기 때문에 좀 까다롭다.
이 상황에서 최선의 방법은 함수에서 volumeCredits의 복제본을 초기화한 뒤 계산 결과를 반환하도록 하는 것이다.
값을 누적하는 라인을 별도의 함수로 추출하고 이름을 지어주었다.
위에서 추출한 함수를 사용하도록 변경했다.
컴파일-테스트-커밋 뒤, 새로 추출한 함수에서 쓰이는 변수들 이름을 적절히 바꿔준다.
리턴값을 result로 바꿔주고, perf를 aPerformance로 바꿔주었다.
format 변수 제거하기
다시 최상위 코드인 statement()를 살펴보자.
앞에서 설명했듯이 임시 변수는 나중에 문제를 일으킬 수 있다.
임시 변수는 자신이 속한 루틴에서만 의미가 있어서 루틴이 길고 복잡해지기 쉽다.
따라서 다음으로 할 리팩터링은 이런 변수들을 제거하는 것이다.
그중에서 format이 가장 만만해 보인다.
현재 format은 임시 변수에 (함수 포인터처럼) 함수를 대입한 형태인데, 마틴 파울러는 함수를 직접 선언해 사용하도록 바꾸는 편이라고 한다.
그런데 이름이 마음에 안든다.
- "format"은 이 함수가 수행하는 일을 충분히 설명하지 못한다.
- "formatAsUSD"라고 하기에는 또 너무 장황하다.
이 함수의 핵심은 화폐 단위 맞추기이니, 이 특징을 잘 살리는 느낌을 골라서 적절히 로직과 이름을 바꾸자.
함수 이름을 usd로 바꾸고, usd로 숫자를 바꾸는 계산을 이 안으로 옮겼다.
이름짓기는 중요하면서도 쉽지 않은 작업이다.
리팩터링은 이름을 잘 지어야만 효과가 있다.
물론 단번에 좋은 이름을 짓기는 쉽지 않다.
따라서 처음에는 당장 떠오르는 최선의 이름을 사용하다가, 나중에 더 좋은 이름이 떠오를 때 바꾸는 식이 좋다.
흔히 코드를 두 번 이상 읽고 나서야 가장 적합한 이름이 떠오르곤 한다.
volumeCredits 변수 제거하기
마저 volumeCredits을 다뤄보자.
이 변수는 반복문을 한 바퀴 돌 때마다 값을 누적하기 때문에 리팩터링하기가 더 까다롭다.
따라서 먼저 반복문 쪼개기(8.7절)로 volumeCredits값이 누적되는 부분을 따로 빼낸다.
상수배 시간복잡도는 고려하지 말아보자.
이어서 문장 슬라이드하기(8.6절)를 적용해서 volumeCredits 변수를 선언하는 문장을 반복문 바로 앞으로 옮긴다.
이제 이러면 임시 변수를 질의 함수로 바꾸기(7.4절)가 수월해진다.
이번에도 volumeCredits 값 계산 코드를 함수로 추출(6.1절)하는 작업부터 한다.
이후 volumeCredits변수를 인라인할 차례이다.
여기서 잠시 멈추고, 방금 한 일을 다시 생각해보자.
반복문을 쪼개서 성능이 느려지지 않을까 걱정할 수 있다.
이처럼 반복문이 중복되는 것을 꺼리는 이들이 많지만, 이 정도 중복은 성능에 미치는 영향이 미미할 때가 많다.
- 경험 많은 프로그래머조차 코드의 실제 성능을 정확히 예측하지 못한다.
- 똑똑한 컴파일러들은 최신 캐싱 기법 등으로 무장하고 있어서 우리의 직관을 초월하는 결과를 내어주기 때문이다.
- 소프트웨어 성능은 대체로 코드의 몇몇 작은 부분에 의해 결정되므로 그 외의 부분은 수정한다고 해도 성능 차이를 체감할 수 없다.
물론 '대체로 그렇다'와 '항상 그렇다'는 엄연히 다르다.
때로는 리팩터링이 성능에 상당한 영향을 주기도 한다.
그런 경우라도 나는 개의치 않고 리팩터링한다.
잘 다듬어진 코드라야 성능 개선 작업도 훨씬 수월하기 때문이다.
이 과정에서 리팩터링된 코드를 예전으로 되돌리는 경우도 있지만, 대체로 리팩터링 덕분에 성능 개선을 더 효과적으로 수행할 수 있다.
리팩터링으로 인한 성능 문제는 '특별한 경우가 아니라면 일단 무시하라'!
리팩터링 중에 만약 테스트가 실패하고 원인을 바로 찾지 못하면, 가장 최근 커밋으로 돌아가서 테스트에 실패한 리팩터링의 단계를 더 작게 나눠 다시 시도해라.
코드가 복잡할수록 단계를 작게 나누면 작업 속도가 빨라진다.
이와 같이 totalAmount도 리팩토링해보자.
리팩터링 결과는 아래와 같다.
중간 점검: 난무하는 중첩 함수
잠시 멈춰서 전체 리팩터링 결과를 다시 한번 돌아보자.
코드 구조가 한결 나아졌다.
최상위의 statement 함수는 이제 단 7줄 뿐이며, 출력할 문장을 생성하는 일만 한다.
계산 로직은 모두 여러 개의 보조 함수로 빼냈다.
결과적으로 각 계산 과정은 물론 전체 흐름을 이해하기가 훨씬 쉬워졌다.
계산 단계와 포맷팅 단계 분리하기
이제 statement()의 HTML 버전을 건드릴 수 있을 정도로 코드가 단순해졌다.
그런데 한가지 문제가 있다.
모든 계산 함수들이 statement()의 중첩 함수로 들어가있다.
이것들을 어떻게 리팩토링하면 좋을까?
마틴 파울러는 "단계 쪼개기"(6.11절)을 추천한다.
- statement() 에 필요한 데이터를 먼저 처리한다.
- 앞서 처리한 결과를 텍스트나 HTML로 표현하도록 만든다.
즉, 첫번째 단계에서는 두번째 단계로 전달할 중간 데이터 구조를 생성하는 것이다.
우선 본문 전체를 renderPlainText() 로 추출하자.
다음으로 두 단계 사이의 중간 데이터 구조 역할을 할 객체를 만들어서 renderPlainText()에 인수로 전달한다.
리팩토링 과정 도중에 발생하는 중복은 허용하라.
일단은 만들고, 나중에 제거하라 - 테스트 주도 개발 -
이번에는 renderPlainText()의 다른 두 인수(invoice, plays)를 살펴보자.
이 인수들을 통해 전달되는 데이터를 모두 방금 만든 중간 데이터 구조로 옮기면, 계산 관련 코드는 전부 statement() 함수로 모으고 renderPlainText()는 data 매개변수로 전달된 데이터만 처리하게 만들 수 있다.
가장 먼저 invoice에 담긴 데이터를 제거해보자.
이제 연극 제목도 중간 데이터 구조에서 가져오도록 만들자.
이를 위해 공연 정보 레코드에 연극 데이터를 추가해야 한다.
여기서 enrichPerformance는 얕은 복사를 수행해 데이터를 건넨다.
이렇게 얕은 복사를 수행한 이유는 함수로 건넨 데이터를 수정하기 싫어서다.
이제 연극 정보를 담을 자리가 마련됐으니 실제로 데이터를 담아보자.
이를 위해 함수 옮기기(8.1절)를 적용하여 playFor() 함수를 statement()로 옮긴다.
이후 renderPlainText() 안에서 playFor()를 호출하던 부분을 중간 데이터를 사용하도록 바꾼다.
(이부분은 각자 바꾸길 바란다.)
이어서 amountFor()도 비슷하게 바꾼다.
다음으로 적립 포인트 계산 부분을 옮긴다.
마지막으로 총합을 구하는 부분을 옮긴다.
이렇게 옮기고 나니, 가볍게 반복문을 파이프라인으로 바꾸기(8.8절)까지 적용하고 싶어졌다.
이제 첫 단계인 'statement()에 필요한 데이터 처리'에 해당하는 코드를 모두 별도 함수로 빼낸다.
두 단계가 명확히 분리됐으니 각 코드를 별도 파일에 저장한다.
이제 HTML 버전 출력을 작성하자.
중간 점검: 두 파일(과 두 단계)로 분리됨
지금까지 짠 코드를 돌아보자.
원래 44줄짜리 코드가 지금은 htmlStatement()를 빼고도 70줄이나 된다.
늘어난 주된 원인은 함수로 추출하면서 함수 본문을 열고 닫는 괄호가 덧붙었기 때문이다.
추가된 코드 덕분에 전체 로직을 구성하는 요소 각각이 더욱 뚜렷이 부각되고, 계산하는 부분과 출력 형식을 다루는 부분이 분리됐다.
이렇게 모듈화하면 각 부분이 하는 일과 그 부분들이 맞물려 돌아가는 과정을 파악하기 쉬워진다.
간결함이 지혜의 정수일지 몰라도, 프로그래밍에서만큼은 명료함이 진화할 수 있는 소프트웨어의 정수다.
모듈화한 덕분에 계산 코드를 중복하지 않고도 HTML 버전을 만들 수 있었다.
캠핑자들에게는 "도착했을 때보다 깔끔하게 정돈하고 떠난다"는 규칙이 있다.
프로그래밍도 마찬가지다.
항시 코드베이스를 작업 시작 전보다 건강하게(healthy) 만들어놓고 떠나야 한다.
다형성을 활용해 계산 코드 재구성하기
잠시 amountFor 메소드를 보자.
현재 switch-case 문으로 조건문을 처리하고 있는데, 객체지향에는 다형성이라는 유능한 도구가 있다.
지금부터 조건부 로직을 다형성으로 바꾸기(10.4절)를 적용해보자.
공연료 계산기 만들기
statement() 에 필요한 데이터를 먼저 처리하는 과정의 핵심은 각 공연의 정보를 중간 데이터 구조에 채워주는 enrichPerformance()
함수다.
현재 이 함수는 조건부 로직을 포함한 함수인 amountFor()와 volumeCreditsFor()를 호출하여 공연료와 적립 포인트를 계산한다.
이를 이제 전용 클래스 PerformanceCalculator로 옮겨보자
현재 이 클래스로 크게 할 수 있는것은 없다.
모든 데이터 변환을 한 곳에서 수행하게 만들기 위해, 기능들을 하나씩 계산기에 넣어보자.
그 전에, 연극 정보를 계산기로 넘겨주는 코드를 추가해두자.
함수들을 계산기로 옮기기
지금까지는 중첩 함수를 재배치하는 것이어서 함수를 옮기는데 큰 부담이 없었다.
하지만 이번에는 함수를 (모듈, 클래스 등) 다른 컨텍스트로 옮기는 큰 작업이다.
그러니 이번에는 함수 옮기기(8.1절) 리팩터링으로 작업을 단계별로 차근차근 진행해보자.
가장 먼저 할 일은 공연료 계산 코드를 계산기 클래스 안으로 복사하는 것이다.
같은 방식으로 적립 포인트를 계산하는 코드도 옮겨주자.
이후 enrichPerformance에 존재하는 원래 함수를 인라인(6.2절)하여 새 함수를 직접 호출하도록 하자.
공연료 계산기를 다형성 버전으로 만들기
이제 본격적으로 다형성을 이용해보자.
- 팩토리 함수에서 적절한 계산기를 선택하는 작업을 수행한다.
- 계산기는 주어진 역할에 맞춰 적절한 계산을 수행하도록 만든다.
이제 이 계산기 팩토리 메소드를 적절하게 바꿔보자.
이제 각 계산기가 직접 계산을 수행하도록 로직을 옮겨주자.
그럼 이제 상위 클래스인 PerformanceCalculator는 다음과 같이 수정할 수 있어진다.
마무리로 적립 포인트 계산 로직들을 리팩토링해보자.
연극 장르들을 검토한 결과, 일부 장르에서만 약간씩 다를 뿐 대다수의 연극은 관객 수가 30을 넘는지 검사해야 한다.
이럴 때는 일반적인 경우를 기본으로 삼아 슈퍼클래스에 남겨두고, 장르마다 달라지는 부분은 필요할 때 오버라이드하게 만드는 것이 좋다.
그래서 희극 처리 로직을 해당 서브클래스로 내린다.
상태 점검: 다형성을 활용하여 데이터 생성하기
앞에서 함수를 추출했을 때처럼, 이번에도 구조를 보강하면서 코드가 늘어났다.
- 연극 장르별 계산 코드를 함께 묶어놨다.
앞으로의 수정 대부분이 이 코드에서 이뤄질 것 같다면 이렇게 명확히 분리해두면 좋다.
본 글은 마틴 파울러의 리팩터링을 참고하여 작성되었습니다.
리팩터링 2판 - 예스24
개발자가 선택한 프로그램 가치를 높이는 최고의 코드 관리 기술마틴 파울러의 『리팩터링』이 새롭게 돌아왔다.지난 20년간 전 세계 프로그래머에게 리팩터링의 교본이었던 『리팩토링』은,
www.yes24.com