목차)
1. 잘 작동하는 코드
2. 읽기 쉬운 코드
3. 일관된 코드
4. 테스트가 용이한 코드
5. 지속 가능한 코드
시간이 지나면 자연스레 먼지가 쌓이고 청소를 해야 합니다. 재 때 치우지 않고 청소를 미루다 보면 어느새 먼지는 점점 쌓여 대청소를 해야 하는 순간이 옵니다. 코드 관리도 마찬가지입니다. 사용하다 보면 점점 복잡해지고 관리를 미루다 보면 나중에 가서 새로 작성하는 것만큼의 시간과 비용이 발생할 수 있습니다. 그렇기에 평소에 코드를 잘 관리하고 좋은 코드를 유지하기 위해 노력해야 합니다.
그렇다면 좋은 코드란 무엇일까요?
좋은 코드에 딱 정해진 정답은 없습니다. 각자의 가치관, 선호도 등에 따라 답은 달라질 수 있죠. 이번 포스팅은 안드로이드 개발자의 개인적인 주관을 담아 좋은 코드란 무엇인지 고민한 글입니다. 평소 좋은 코드에 대해 고민하셨던 분들 또는 고민을 시작하시는 분들에게 많은 도움이 됐으면 좋겠습니다.
1. 잘 작동하는 코드
잘 작동하는 코드는 좋은 코드를 위한 첫 단계로 가장 기본적이지만 간과하기 쉬운 조건입니다. 코드의 목적은 주어진 명령을 수행하는 것입니다. 명령을 정확히 수행하며, '잘' 작동해야 합니다. 단순히 그냥 작동하는 것이 아닌 어느 상황에서나 잘 작동하는 코드가 좋은 코드라고 말할 수 있습니다. 코드가 본인이 생각한 의도대로 잘 작동하는지 끊임없이 의심하고 검증해야 합니다. 또한 다양한 환경을 고려해 테스트를 하고 오류를 파악할 수 있어야 합니다.
안드로이드에서는 비정상 종료를 파악하기 위해 Android vitals, Firebase Crashlytics를 사용할 수 있으며, crash 및 이벤트 로그 수집을 위해서는 sentry를 이용하는 방법이 있습니다.
2. 읽기 쉬운 코드
좋은 코드의 또 다른 특징은 가독성입니다. 혼자 개발한다면 상관없겠지만 여러 명이서 협업할 경우 가독성은 매우 중요한 부분입니다. 읽기 쉬운 코드는 협업하는 개발자들에 대한 배려이며 코드 해석 시간을 줄어줍니다. 또한 향후 유지 보수 및 확장, 재사용을 쉽게 해줍니다. 가독성 좋은 코드를 만들기 위해 코드 및 네이밍 규칙과 주석을 잘 활용하는 방법이 있습니다.
코드 규칙은 각 언어마다 권장되는 스타일 가이드를 참고하면 되며, 코드 작성 후 스타일 검사를 해주는 것이 좋습니다. 코드 규칙 예시로 코틀린 코딩 컨벤션을 참고해 주세요. 또한 안드로이드 코틀린의 경우 ktlint 로 스타일 검사를 할 수 있습니다.
네이밍 규칙은 단순하고, 직관적이고, 일관적이어야 합니다. 예시로 Google Cloud 네이밍 컨벤션을 참고해 주세요.
저는 안드로이드를 개발할 때 아래와 같은 네이밍 규칙을 사용합니다.
파일은 Pascal 표기법, 함수, 변수명은 Camel 표기법을 사용합니다.
- 파스칼 표기법 (Pascal Case) : 단어 첫 글자를 대문자로 작성
ex) MainActivity, HomeFragment
- 카멜 표기법 (Camel Case) : 단어 첫 글자를 대문자로 작성, 맨 앞 글자는 소문자로 작성
ex) setupObservers(), viewModel
안드로이드 리소스에도 네이밍 규칙을 정해서 사용하고 있습니다. 규칙은 아래 링크에 작성해두었습니다.
주석 또한 코드 해석 시간을 줄여주는데 큰 도움이 됩니다. 하지만 주석을 많이 작성하는 것이 결코 좋은 방법은 아닙니다. 너무 적은 주석은 불친절하고 과도한 주석은 코드의 가독성을 떨어뜨리게 되니 적절하게 주석을 사용해야 합니다. 간결하면서도 핵심을 바로 알 수 있도록 명확하게 표현해야 합니다.
파일 맨 앞부분에 아래와 같은 공통 규칙을 정해 사용할 수 있습니다.
/**
* @file 제목
* @brief 간단한 요약 설명
* @details 상세 설명
*
* @author 작성자 이메일 (최종 수정자)
* @date 날짜 (최종 수정일)
* @version 버전
*/
함수도 필요한 부분에 아래와 같이 주석을 사용할 수 있습니다.
/**
* @brief 간단한 요약 설명
* @date 날짜 (최종 수정일)
* @return 리턴 값 설명
* @param 함수 인자 설명
*/
또한 한 줄 주석에 주석 키워드를 활용하는 것이 좋습니다.
//TODO: 해야 할 작업
//FIXME: 문제가 있어 미래에 해결해야 할 코드
//HACK: 아름답지 않은 해결책
//XXX: 큰 문제가 포함된 코드
3. 일관된 코드
때로는 일관성이 구속처럼 느껴질 수 있지만 미래에 더 많은 가치를 가져다주고 협업 시 큰 효과를 발휘합니다. 다른 팀원들이 코드를 봤을 때 '어떻게' 표현했는가 보다 '무엇을' 수행하느냐에 집중할 수 있게 됩니다. 익숙한 코드 형식 속에서 빠르게 문제를 파악하고 이해할 수 있게 됩니다. 또한 일관된 코드는 모듈화하거나 중복을 찾기도 쉽고 규모를 확장하기 쉽게 도와줍니다. 일관성 있는 코드를 위해 아키텍처, 디자인 패턴 등을 사용하고 들여 쓰기, 한 줄 최대 길이, 임포트문 순서 등 규칙들을 정할 수 있습니다.
4. 테스트가 용이한 코드
테스트는 많은 개발자들이 중요하게 생각하는 요소입니다. 테스트 코드는 의도된 역할을 잘 수행하는지 검증하고 빠르게 테스트하며 개발 과정에서 문제를 조기에 파악할 수 있게 됩니다. 따라서 좋은 코드는 테스트가 용이해야 합니다.
테스트하기 쉬운 코드는 다음과 같은 특징을 갖고 있습니다.
1) 결정적인 코드
= 같은 입력에 항상 같은 결과를 반환하는 코드
2) 외부 상태를 변경하지 않는 코드
= 부수 효과가 없는 코드
테스트가 어려운 코드는 다음과 같은 특징을 갖고 있습니다.
1) 비 결정적인 코드
= 실행 시점마다 결과가 달라지는 코드
2) 외부 상태를 변경하는 코드
= 부수 효과가 있는 코드
테스트가 용이한 코드 작성을 위해 테스트하기 쉬운 코드와 어려운 코드를 적절히 분리해야 합니다.
5. 지속 가능한 코드
'구글 엔지니어는 이렇게 일한다' 책에서 소프트웨어 엔지니어링에 대해 아래와 같이 표현합니다.
소프트웨어 엔지니어링은 흐르는 시간 위에서 순간순간 프로그래밍을 모두 합산한 것이다.
시간이 프로그램에 미치는 영향을 알아보려면 '이 코드의 예상 수명은?'이라는 질문을 던져보면 좋습니다. 몇 분 후면 사라질 코드부터 수십 년을 살아남을 코드까지 다양한 경우를 상상해 볼 수 있습니다. 짧은 생을 가진 코드는 대체로 시간의 영향을 받지 않지만 코드의 수명이 길어질수록 변경이라는 요소가 점점 중요해집니다.
지속 가능한 코드를 만들기 위해 어떤 노력을 해야 할까요?
1) 모듈화
모듈화를 통해 코드를 더 효율적으로 유지 관리할 수 있게 됩니다. 코드를 모듈로 분할함으로써 재사용하거나 확장하는데 유리해집니다. 또한 협업할 때 모듈별로 나눠 개발할 수 있어 충돌을 최소화할 수 있고, 모듈별로 빠르게 테스트할 수 있습니다. 안드로이드에서는 클린 아키텍처 구조로 모듈을 나누거나 feature 기능 단위로 모듈을 나누는 방법이 있습니다.
2) 높은 응집력
응집력은 모듈이나 클래스에서 요소들이 얼마나 모여 있는지를 나타내는 개념입니다. 응집력이 높은 모듈. 클래스는 관련된 기능을 수행하기 위한 코드들이 모여 있습니다. 다른 코드와의 의존성을 최소화하고, 내부의 버그를 찾기 쉽고 유지 보수하기 쉽습니다. 이에 반해, 응집력이 낮은 모듈. 클래스는 여러 개의 기능들을 처리하기 위한 코드가 섞여 있으며, 외부와의 의존성이 높아져 유지 보수하기 어렵습니다.
응집력을 높이기 위해서는, 수행하는 기능에 대해 명확히 이해하고 분류해, 비슷한 기능의 코드를 한곳에서 구성해야 합니다. 불필요한 코드를 제거하고, 함수와 변수를 최소한으로 유지하는 것도 중요합니다. 잘 응집된 코드인지 파악하기 위해 코드의 import 문을 확인하는 방법이 있습니다. import 문의 패키지 구조를 확인하며 응집 전략을 세울 수 있습니다. 불필요하게 패키지가 분산되어 있거나, 패키지가 너무 늘어나게 되면 새로운 응집이 필요하다는 신호로 해석할 수 있습니다. 자세한 내용은 토스 - 지속 성장 가능한 코드를 만들어가는 방법을 참고해 주세요.
3) 느슨한 결합도
결합도는 모듈이나 클래스 사이에서 서로 얼마나 의존적인지를 나타내는 개념입니다. 결합도가 낮은 코드는 다른 모듈. 클래스와의 의존성이 낮으며, 독립적으로 개발할 수 있습니다. 이에 반해, 결합도가 높은 코드는 다른 모듈. 클래스와 강하게 의존하며, 수정하기 어렵고 유지 보수 비용이 증가합니다.
결합도를 낮추기 위해서는 다른 모듈. 클래스의 함수나 변수를 최대한 사용하지 않는 것이 좋습니다. 만약 다른 모듈. 클래스의 함수나 변수를 사용해야 한다면, 인터페이스를 명확히 정의하고, 의존성을 최소화하는 것이 좋습니다. 의존성을 최소화하기 위해 의존성 주입(Dependency Injection) 패턴을 사용하는 것이 좋습니다. 의존성 주입을 사용하면, 객체가 직접 다른 객체를 생성하지 않고, 외부에서 객체를 주입받아 사용할 수 있습니다. 이렇게 하면, 객체 간의 의존성이 최소화됩니다.
지금까지 좋은 코드에 대한 생각을 정리해봤습니다.
좋은 코드를 작성한다는 것은 쉽지 않습니다. 하지만 좋은 코드는 미래에 유지 보수, 확장성 등 여러 가지 방면에서 더 큰 보상을 줍니다. 좋은 코드를 작성함으로써 우리는 다른 개발자들과 함께 효율적으로 일할 수 있게 되고 개발 기간 단축에 기여할 수 있게 됩니다.
마지막으로, 좋은 코드는 한 명의 개발자가 만들어낼 수 있는 것이 아닙니다. 다른 개발자들과 협업하고, 다른 사람들의 코드를 이해하고, 적절한 코드 리뷰를 수행하며, 코드의 품질을 높이기 위해 노력하는 것이 중요합니다. 이러한 노력이 모여서 우리는 더 나은 코드와 더 나은 소프트웨어 제품을 만들어낼 수 있습니다.
방문해 주셔서 감사합니다^^
잘 읽으셨다면 좋아요❤ 눌러주세요~!
오늘도 좋은 하루 보내세요 :)
'Think' 카테고리의 다른 글
[창업 일기] 두 번의 값진 실패 경험 (2) | 2022.10.18 |
---|---|
디자이너와 협업을 잘 하기 위해서 (1) | 2022.08.12 |
댓글