반응형

 


 이번에는 최근에 읽은 책인 '읽기 좋은 코드가 좋은 코드다'에 대해서 리마인드도 할겸 조금이라도 생각하게 만들었던 문장들에 대해 정리해보는 시간을 가지도록 하겠습니다. 책을 읽으면서 그동안 저의 안좋았던 코딩 습관들에 대해서 다시 한 번 돌아보는 시간을 가졌고 뭔가 어렵게 짜려고 하기 보다는 심플하면서도 읽기 좋은 코드를 짜도록 노력해야겠다고 다짐하며 정리를 시작하도록 하겠습니다. 가볍게 한 번 읽어주시면 좋을 것 같습니다.



⇒ 코드는 다른 사람이 그것을 이해하는 데 들이는 시간을 최소화하는 방식으로 작성되어야 한다.


⇒ Alt + / = 자동완성(이클립스) >> 그동안 ctrl+space를 많이 사용했는데 옆의 명령어는 바로 자동완성을 해주는 기능


⇒ 팀에 새로 합류한 사람이 이름이 의미하는 바를 이해할 수 있을까? 만약 그렇다면 그 이름은 괜찮은 것이다. (네이밍의 중요성, 내 주관적인 의미에만 집중해서 작성하지 않기!!!)


⇒  대상을 자세히 묘사하는 구체적인 이름을 사용하라. ex) ServerCanStart()보다는 CanListenOnPort()


⇒ 변수명에 중요한 세부 정보를 덧붙여라. 예를 들어 밀리초의 값을 저장하는 변수 뒤에 _ms를 붙이거나 이스케이핑을 수행하는 변수의 앞에 raw_를 붙이는 것이다. 


⇒ 본인이 지은 이름을 "다른 사람들이 다른 의미로 해석할 수 있을까?"라는 질문을 던져보며 철저하게 확인해야 한다.


⇒ 경계를 포함하는 한계 값을 다룰 때는 min과 max를 사용하라.(start, end보다는 경계를 표현하기 위해서는 min, max가 더 정확)


⇒ 주석의 목적은 코드를 읽는 사람이 코드를 작성한 사람만큼 코드를 잘 이해하게 돕는 데 있다.


⇒ 코드에서 빠르게 유추할 수 있는 내용은 주석으로 달지 말자.(너무 뻔한 내용들에 대해서는 주석으로 남기지 않는다.)


⇒ 일반적으로 사람들은 코드가 가진 나쁜 가독성을 메우려고 노력하는 '애쓰는 주석'을 원하지 않는다. 프로그래머들은 이러한 규칙을 대개 좋은 코드 > 나쁜 코드 + 좋은 주석이라는 공식으로 설명한다. (나쁜 코드를 짜고 주석을 다는 방식이 아닌 누가 봐도 이해할 수 있는 좋은 코드를 짜는데 집중하자)


⇒ 좋은 주석은 단순히 '자신의 생각을 기록하는 것' 만으로도 탄생할 수 있다. 즉, 코딩할 때 생각했던 중요한 생각을 기록하면 된다. ex) 놀랍게도, 이 데이터에서 이진트리는 해시테이블보다 40%정도 빠르다. 


⇒ 작성하는 코드의 이러저러한 내용을 훗날 수정할 거라는 생각이 들면, 그러한 생각을 주석으로 작성하는 일은 당연하게 받아들여야 한다는 사실이다. 주석은 코드를 읽는 사람에게 코드의 질이나 상태 그리고 추후 개선 방법 등을 제시하여 소중한 통찰을 제공한다.


⇒ 상세하고 공식적인 문서를 작성해야 한다는 생각에 압도당하지 말라. 잘 선택된 몇몇 문장이 아무것도 없는 것보다는 훨씬 나은 법이다.


⇒ 주석을 다는 목적은 코드를 작성하는 사람이 알고 있는 정보를 코드를 읽는 사람에게 전달하는 것이다. 


[ 주석으로 설명하지 말아야 하는 것 ]


:: 코드 자체에서 재빨리 도출될 수 있는 사실


:: 나쁜 함수명과 같이 나쁘게 작성된 코드를 보정하려고 '애쓰는 주석'. 그렇게 하는 대신 코드를 수정하라.



[ 주석으로 기록해야 하는 것 ]


:: 코드가 특정한 방식으로 작성된 이유를 설명해주는 내용


:: 코드에 담긴 결함. TODO:혹은 XXX:와 같은 표시를 사용


:: 어떤 상수가 특정한 값을 갖게 된 '사연'


⇒ 기본적으로 if/else를 이용하라. ?: 를 이용하는 삼항 연산은 매우 간단할 때만 사용해야 한다. (삼한 연산자로 표현은 간결해지지만 오히려 읽기 이해하기 힘들어지는 연산들이 있다. 이때는 삼한 연산자보다는 읽기 쉽게 if/else를 사용하여 코딩하도록 하자.)


⇒ "전역 변수를 피하라". 전역 변수는 어디에서 어떻게 사용되는지 일일이 확인하기 어려우므로 이는 합당한 조언이다. 또한, 전역 변수의 이름과 지역 변수의 이름이 중복되어 이름공간이 더러워 질 수도 있고, 어떤 코드가 지역 변수를 변경할 때 실수로 전역 변수를 변경하거나 혹은 그 반대의 경우가 일어 날 수 있다.


⇒ 할머니에게 설명할 수 없다면 당신은 제대로 이해한 게 아닙니다. -알버트 아인슈타인 (참 맞는말이다. 무엇인가를 이해했다는 건 해당 지식이 전무한 사람에게도 설명할 수 있어야 한다. 그만큼 제대로 된 이해는 중요하다 )


⇒ 복잡한 생각을 다른 사람에게 설명할 때 중요하지 않은 자세한 내용 때문에 듣는 사람을 혼동시키는 일이 종종 있다. '쉬운 말'로 자신의 생각을 지식이 부족한 사람에게 전달하는 기술은 매우 소중하다. (누군가에게 설명하기 위해서는 해당 부분에 대한 완벽한 이해가 수반되어야 함을 명심하자. 이해하면 설명하고 싶어진다 by 김동범)


⇒ 일반적인 '유틸리티'를 많이 생성하여 중복된 코드를 제거하라.


⇒ 사용하지 않는 코드 혹은 필요 없는 기능을 제거하라.


⇒ 코드를 제거하는 작업을 코딩을 하는데 투자한 시간이 쓸모 없는 시간이었다고 여기는 사고를 버려야 한다. 프로그래밍은 창의력을 요구하는 분야다. 사진사, 작가, 영화감독 같은 사람은 모든 작업 결과를 보존하지 않는다. (코드 리무버가 되기 위해서는 해당 소스 코드에 대한 정확한 이해와 판단이 필요하다. 즉 아무나 코드를 제거할 수 없다.)


⇒ 매일 15분씩 자신의 표준 라이브러리에 있는 모든 함수/모듈/형들의 이름을 읽어라. 라이브러리 전체를 암기하라는 게 아니다. 그냥 그 안에 무엇이 있는지 감을 잡아놓고, 나중에 새로운 코드를 작성할 때 "잠깐만, 이건 전에 API에서 보았던 것과 뭔과 비슷한데..."하고 생각할 수 있기를 바라는 것이다.


⇒ 다른 프로그래머는 종종 테스트 코드를 실제 코드가 어떻게 동작하며 어떻게 사용되어야 하는지에 관한 비공식적인 문서라고 생각한다. 따라서 테스트 코드가 읽기 쉬우면, 사용자는 실제 코드가 어떻게 동작하는지 그만큼 더 쉽게 이해할 수 있다. (테스트 코드의 중요성)


이렇게 리마인드 할 겸 내용을 정리해 보았는데 정리하면서 다시 한 번 머리에 각색이 된 것 같다. 앞으로도 꾸준히 코드를 작성함에 위의 내용들에 유의하며 작성할 수 있도록하고 꾸준히 해당관련 도서 (리팩토링, 클린코드)등을 읽으며 리마인드 해보는 것도 많이 도움이 될 것 같다. 이상으로 포스팅을 마친다.


 




반응형
반응형



 부서를 처음 배정받고 선배님들이 'Legacy 코드'라는 말을 자주 사용하는 것을 들을 수 있었다. 그 당시에는 Legacy코드가 뭔지에 대해서 감도 전혀 없었고 Legacy 코드에서 작업하는 것이 새롭게 프로젝트를 진행하는 것보다 어려운지 또한 알지 못했다. 하지만 요즘 Legacy 코드 위에서 작업을 하면서 몸소 그 어려움에 대해 체험하는 중이다. 하지만 반면으론 현재 서비스되고 있는 거대한 양의 코드들을 다루며 내가 그동안 프로그래밍을 하면서 알지 못했던 방식이나 방법들에 대해서도 프로그래밍을 함에 있어 지향해야 할 부분과 지양해야 할 부분에 대해서도 많이 생각해보고 적용해보며 견문을 넓혀 가고 있는 중이다.그렇다면 Legacy코드가 무엇인지에 대해서 알아보고 그 위에서 어떻게 작업해가면 좋을지에 대해서 살펴보도록하자

 Legacy란???먼저 Legacy의 사전적인 의미로는 "(남이 남긴)유산"으로 해석할 수 있다. 그럼 Legacy코드란? 많은 사람들이 이에 대해 다양한 정의를 내리고 있지만 간단히 말해 '다른 사람에게 넘겨받은 읽기 어렵고 수정하기 어려운 오래된 코드'를 말한다. 특히 기술의 변화가 많은 웹 프로젝트들에 있어서는 시간이 지나면서 기술이 발전할 수록 Legacy 코드들이 점점 쌓여저만 간다. 그렇다면 Legacy 코드 위에서 작업할 때 부딪히는 문제들에는 어떠한 것들이 있을까?


 Legacy Code에서 작업할 때 부딪히는 문제점은??? 첫번 째, 해당 코드를 이해하기 어려운 문제점이 있다. (변수명, 메소드명이 명확하지 않거나 매우 복잡함)

 두번 째, 해당 코드에 대해 완벽하기 이해하기가 어렵기 때문에 수정이 필요한 곳도 쉽게 수정하기 힘들어 진다. 또한 해당 부분을 수정한다고 했을 때 그에 따른 사이드 이펙트(기능 수정으로 인해 예상치 못한 부분에서 에러 발생)가 어디에서 발생할지에 대해서도 예측하기가 힘들다.

 세번 째, 코드에 대해 이해가 힘들다보니 해당 프로젝트에 대한 유지보수 작업을 진행함에 있어 자신감이 하락하게 된다.

 그렇다면 Legacy 코드를 만들지 않기 위해서는 어떻게 해야할까??? 1. Magic Number를 사용하지 말자.

 ①번 방식처럼 사용하게 될 경우 숫자1과 숫자2과 무엇을 의미하는 코드인지 알기 힘들다 ②번 방식처럼 Magic Number를 제거하고 사용하게 되면 좀 더 이해하기 쉬운 코드를 짤 수 있다.


 2. 의도를 나타내는 이름을 사용하자. 예를 들어 다음과 같은 메소드가 있다고 하자. (밑의 코드를 예를 들기 위해 짠 코드이기 때문에 메서드 명에 집중해주기 바란다)

 위 메서드가 어떠한 작업을 하는지 메서드명만 봐서는 도무지 감을 잡을 수가 없다. 반면 밑의 코드를 보면

 해당 메소드가 무엇을 하는 메소드인지 단번에 알 수 있다. 이렇든 명확한 이름을 사용하여 코드를 이해하는데 드는 시간을 줄일 수 있다.


 3. 가급적 프로그래밍을 할 때 부분만 옳은 것보다 전체적 대칭을 지키자. 예를 들어,district.setDong("정자")district.setGu("분당")district.setMetroPolitanOrSi("성남") 와 같이 부분만 옳은 동떨어진 메서드를 사용하기 보다는district.setDong("정자")district.setGu("분당")district.setSi("성남") 와 같이 전체적 대칭을 지키는게 나을 때가 많다. 따라서 기존의 코드와 대칭을 이루는 메서드명을 사용하는게 명칭이 잘못됬다 할지라도 나을 경우가 있다.


 4. if문이 덕지덕지 붙어있는 복잡한 조건식을 피하자.될 수 있으면 조건문을 사용하기 보단 다양한 패턴들을 활용해 복잡한 조건식을 피하고 메서드를 세분화 시켜주자.


 5. 사용하면 안 되는 클래스/메서드가 있을 경우

위와 같이 deprecated를 해놓으면 이클립스 등에서 코딩할 때 메서드에 취소선이 나와, 개발자의 주의를 환기시킨다. 하지만 아무런 설명이 없기 때문에 왜 저 메서드를 사용하면 안되는지에 대해 알 수 없다. 그렇기 때문에 무책임하게 @deprecated만 선언하지 말고 왜 @deprecated 되었는지 설명 또한 달아주도록 하자.




 6. 뻔한 중복 주석은 피하자.메서드 명이나 변수명만 봐서도 충분히 파악할 수 있는 부분에 대해서는 추가적인 주석을 달 필요가 없다. 특별한 경우나 이름 만으로만 이해하기 힘든 부분이 있을 경우 꼭 필요한 부분에 있어서 주석을 사용해주자. (의미없는 주석 사용 nono)


 7. 기능에 해당하는 테스트 코드를 작성하자.


 8. 리팩토링 작업을 지속적으로 하자. 리팩토링 - 외부 동작을 바꾸지 않으면서 내부 구조를 개선하는 방법으로, 소프트웨어 시스템을 변경하는 프로세스이다. 리팩토링을 별도의 작업이 아니라 개발의 한 부분으로 생각하도록 하자.


 현장 TIP1. Q) 얼마 전 중복이 많고 복잡한 코드를 GOF 디자인 패턴을 적용하여 리팩토링 했다. 그런데 코드리뷰 때 다른 개발자가 더 코드가 더 보기 어려워졌다고 한다.

 A) Communicative Code의 요건 중 배려가 있다. 따라서 분명히 구조적으로 좋아졌다고 하더라도 팀 내 다른 개발자가 모두 코드 읽기를 어려워 한다면 한번 더 생각해볼 만한 부분이라고 생각한다. 가치의 상충이 일어나는 것인데 '구조적 개선 vs 읽기 어려움'이다. 각각을 통해 얻게 될 가치를 종합적으로 잘 비교해보고 취사선택하는 것이 좋을 것이라 본다. 또한 GOF 디자인 패턴 적용의 초점이 문제해결이 아닌 적용 그 자체에 있을 때가 종종 있다. 이런 형태의 적용은 득보단 실이 많기 때문에 가급적 피하는 것이 좋다.


2. Q) Legacy 코드를 수정 중인데 너무 읽고 이해하기 어려운 클래스 xxx가 있다. 아무래도 안될 것 같아 New XXX를 만들었다. 하지만 예전 클래스를 사용하는 곳 까지 모두 수정하기는 어려워 이번에 작업하는 범위의 클래스에서만 New XXX를 사용하게 바꾸었다. 이렇게 해도 괜찮은 건가?

 A) 경험이 비추어 볼 때 절대 하지 말아야 할 형태의 개선이다. 이런 형태의 개선을 할 때 New, New2, New3가 계속 생기며, 기존 클래스는 정리가 안 되어 이후 복잡도가 높아지고 혼란을 가중시키는 사례를 봐왔다. 따라서 가급적 신규 클래스를 도입하는 형태의 개선은 트랜잭션과 마찬가지로 'All or Noting' 원칙이 적용되어야 한다고 본다. 신규 클래스를 도입하려면 반드시 기존 클래스를 제거하고, 그게 힘들다면 신규 클래스를 만들지 말고 예전 클래스 기반에서 수정하는 것이 올바른 방향이락 본다.


3. Q) Legacy 코드를 수정하기에 앞서 Cover & Modify를 하려고 하고 있다. 그런데 의존하는 객체가 너무 많았다. 할 수 없이 하나하나 만들어가며 테스트를 만들었다. 그런데 새로운 객체를 생성하는 등의 리팩토링을 하다 보니 기존에 만든 테스트가 다 실패한다. 도움을 주기보다는 거추장스러운 느낌이 나는데 왜 그런가?

 A) BO나 Service를 대상으로 단위 테스트를 만들면 대게 객체간의 인터랙션을 테스트하는 코드를 만들게 된다. 하지만 이런 테스트는 객체 간의 인터랙션을 조정하는 등의 리팩토링을 하게 되면 테스트를 고쳐줘야 하고 따라서 위와 같이 의미가 퇴색 될 수 있다. 따라서 이런 때는 통합 테스트를 활용하여 상태를 기반으로 검증하는 것이 좋다. BO의 예를 들면 실제 BO나 Service를 호출하고 데이터베이스 등에 값이 제대로 들어갔는지 등을 검사하는 것이다. 이렇게 만든 테스트는 객체의 관계를 조정하는 등의 리팩토링을 해도 달성되는 결과는 같기 때문에 테스트를 수정할 필요가 없고 안전망으로써 역할을 잘 수행한다.

 지금까지 Legacy코드와 Legacy 코드를 만들지 않기 위한 방법, 현장 Tip등 에대해 알아보았다. 많은 내용은 NHN에서 교육했던 'Legacy 코드에서 작업하기'교재에서 참고하여 정리하였다.


반응형

+ Recent posts