파이썬 2에서 3으로의 변경으로 코드 리팩터링이 불가피한 경우
새로운 언어 버전에서 실행하기 위해 오래된 코드를 분석해야 하는 상황에 직면하는 것은 개발자가 어떤 대가를 치르더라도 피하려고 하는 일 중 하나입니다. 오래된 코드를 이해하기 어렵고, 예기치 않은 변경으로 인해 새로운 버그가 발생할 수 있으며, 성능이 저하될 가능성이 높고, 투자 대비 성과가 거의 없는 엄청난 노력이 필요하기 때문입니다. 결국, 새로운 것을 추가하는 것이 아니라 이전처럼 실행되도록 하는 것입니다. 이 글에서는 이러한 코드 리팩터링을 피할 수 없게 만드는 파이썬 2와 3의 변경 사항 몇 가지 예를 다룹니다.
이제 Python 2 애플리케이션이 의도한 대로 실행되고 있습니다. 몇 년 동안 완벽하게 작동해 왔고, 개발팀이 모든 문제점을 해결했으며, 모두가 성능에 만족하고 있습니다. 더 이상 필요하지 않을 때 작업을 완료하고 방해가 되지 않습니다.
그런데 갑자기 파이썬 2를 겨냥한 새로운 취약점이 발표되었습니다. 공식 지원이 종료되어 더 이상 보안 패치를 사용할 수 없으므로 이제 자신의 잘못이 아니더라도 멋진 애플리케이션도 보안에 취약해져 조직의 보안 책임자가 되었습니다. 부지런한 IT 관리자라면 개발팀에 안전한 버전의 Python 3에서 실행되도록 코드를 우선적으로 리팩터링하는 작업을 지시합니다.
팀에 다른 프로젝트가 진행 중이었는데 이제 모든 프로젝트가 보류되었습니다. 비용이 많이 드는 결정이지만 해야만 하는 일 아닌가요? 필요한 코드 변경 사항과 그 변경 사항이 예상치 못했거나 더 이상 기억나지 않는 다른 코드 섹션에 미치는 영향에 대한 모든 세부 정보를 수집하는 데 몇 주가 걸립니다. 파이썬 2 이후 일부 기능이 사용 중단되었기 때문에 핵심 로직의 상당 부분을 다시 작성해야 하며, 파이썬 3의 대체 기능은 드롭인 방식이 아니므로 새로운 단위 테스트도 만들어야 합니다.
개발자들은 더 이상 조직에 남아 있지 않은 전 동료가 작성한 제대로 문서화되지 않은 코드를 이해하기 위해 고군분투하며 몇 주가 또 지나갑니다. 코드의 일부가 "그냥 작동"하는 것 같지만 더 이상 아무도 그 이유를 이해하지 못합니다. 아, 그리고 몇몇 종속성은 Python 3을 지원하지 않으며 조만간 더 이상 지원되지 않을 것으로 보이는 프로젝트도 있습니다.
수개월간의 노력 끝에 개발팀은 새로운 버전의 애플리케이션을 프랑켄슈타인 빌드로 다시 만들었습니다. 원본과 약간 비슷하지만 일부 UI 선택은 축적된 지식을 사용하기 어렵게 만들어 운영자에게 재교육이 필요합니다.
누군가 데이터베이스에 변경 사항을 적용할 때마다 성능이 저하되는 것 같습니다. 그 이유는 아무도 확실하지 않지만, 처음부터 다시 작성해야 했던 종속성 중 하나가 그 문제를 더 잘 처리했습니다. 그리고 팀이 진행하던 모든 초기 프로젝트는 이제 늦어지고 예산도 초과되었습니다.
팀은 향후 리팩토링 작업에 유용한 지식으로 남길 수 있는 몇 가지 결과를 가지고 돌아왔습니다:
Division:
Python 3: 5/2 == 2.5
파이썬 2: 5/2 == 2 ("/"를 "//"로 두 배로 늘리면 이전 동작이 반환됨)
정수를 나누면 이제 더 이상 정수가 아닌 부동 소수점이 반환됩니다.
인쇄:
파이썬 3: print("")
Python 2: 인쇄 ""
사전이 변경됩니다:
파이썬 3: 딕셔너리 메서드 keys(), items(), values()는 뷰를 반환합니다.
파이썬 2: 해당 메서드는 리스트를 반환합니다. 또한 iterkeys(), iteritems 및 itervalues() 메서드는 더 이상 지원되지 않습니다.
정렬:
Python 3: cmp()를 더 이상 사용하지 않아야 합니다.
텍스트 및 바이너리:
이제 텍스트와 바이너리 데이터는 서로 다른 개념이므로 더 이상 문자열에 저장해서는 안 됩니다. 일부 문자열 연산은 더 이상 문자열에 허용되지 않는 문자가 포함되어 있기 때문에 이전에 작동하던 데이터에서 더 이상 작동하지 않습니다.
튜플:
더 이상 이전처럼 튜플 매개변수를 언패킹할 수 없습니다.
여러 키워드가 제거되었습니다:
“<>” is now “!=”.
정수 리터럴은 더 이상 닫는 l 또는 L과 함께 작동하지 않습니다.
문자열 리터럴은 더 이상 닫는 u 또는 U와 함께 작동하지 않습니다.
이제 모듈에서 모든 것을 가져오는 것은 함수 내부가 아닌 모듈 수준에서만 작동합니다.
그 밖에도 많은 변경 사항이 있습니다(전체 목록은 여기에서 확인할 수 있습니다: https://docs.python.org/3/whatsnew/3.0.html에서 확인할 수 있습니다(이 글의 출처로 사용되었습니다).
이 과정이 끝나면 애플리케이션은 이전보다 더 나빠졌고, 새로운 언어 수준으로 이동하여 얻을 수 있는 실질적인 이점은 없었습니다. 이전 기능을 대체하는 구조만 사용되었고, 새로운 고급 기능은 없었으며, 단지 다시 실행하는 것이 목표였기 때문입니다. 이는 다른 많은 조직 차원의 활동에 영향을 미치고 모든 수준에서 혼란을 야기하는 비용이 많이 드는 작업이었습니다.
대신 무엇을 할 수 있었을까요?
더 나은 대안은 Python에 대한 확장 수명 주기 지원이라는 형태로 존재합니다. 언어 수준의 변경 없이 적시에 보안 수정을 제공했다면 서비스를 배포하는 것만으로도 문제를 해결할 수 있었을 것입니다. 동일한 코드가 그대로 실행되고 보안 취약점이 수정되었을 것이며 다른 모든 중단을 완전히 피할 수 있었을 것입니다.