2025/02/25 ch03. 코딩 스타일
내가 작성하는 코드를 누군가 넘겨받는다는 상상을 하며 코드를 짜 봅시다
나조차 나중에 내 코드를 보면 로직이 기억 안날 수 있다
잘 작성된 코드에서 볼 수 있는 공통적인 속성
- 문서화
- 코드 분해
- 명명 규칙
- 언어 사용
- 코드 서식(포매팅)
코드 문서화
주로 소스 파일에 주석을 다는 것을 의미
사용법을 알려주는 주석
- 클라이언트에서 코드를 사용하는 방법을 알려줌.
함수 이름, 리턴 타입, 매개변수 이름과 타입만 보고도 함수의 기능을 쉽게 파악할 수 있어야 되지만 쉽지 않음.
함수를 호출하기 전,후에 특정 조건을 만족해야 한다면? 주석으로 명시해야함
함수가 던지는 익셉션도 주석으로 남겨야 함
헤더파일에 정의된 공용함수나 메서드는 되도록 주석으로 남기자
함수 이름만봐도 누구나 알 수 있는 정보는 주석으로 남기지 않아도 된다
리턴값은 int와 같은 광범위한 타입보단, 무엇을 리턴하는지 명확하게 알 수 있는 타입을 하나 만들어 두면
주석으로 달지 않아도 된다
함수에 전달할 매개변수나 함수의 리턴값이 너무 광범위한 경우, 구체적으로 어떤 값을 전달하는지 주석으로 달아놓자
공식문서에는 구현에 대한 설명이 아니라 코드의 동작에 대한 설명만 나오면 된다
입력, 출력, 에러, 용법, 성능 보장등을 적으면 되지만
이 함수가 어떤 알고리즘을 사용했는지 자세히 설명할 필요는 없다
복잡한 코드를 설명하는 주석
코드에 적용된 알고리즘이 너무 복잡해 누구나 한눈에 알아보기 힘든경우, 알고리즘에 대한 대략적인 설명을 적는게 좋다
또한 이 알고리즘이나 반복문 내에서 반드시 만족해야 하는 조건(불변조건)을 명시해주는것도 좋다
메타 정보를 제공하는 주석
코드 내용과는 다른 차원의 정보를 제공하기 위한 목적으로 주석을 할 수 있다.
코드 내용에 대해선 표현하지 않고, 코드 생성에 대한 세부사항(코드 작성자, 날짜, 인용 등등..)만 표현한다.
주석 스타일
- 문장 단위 주석
모든 문장에 주석을 작성하면 꼭 필요한 코드만 작성하게 되지만 지나치게 지저분해짐
# 주석으로 적어야될만큼 한 눈에 들어오지 않는 표현식은 그대로 적기보다 쉬운 이름으로 된 함수로 바꾸면 코드 자체가 문서의 역할을 하기 때문에 가독성도 좋아지고 재사용성도 좋아진다.
코드가 난해하다면 주석을 많이 달아두는것이 이해하지 못하는 것보다는 낫다
- 머릿말 주석
소스 파일에 첫머리에 표준 주석으로 시작하도록 정하는 것
작성할 프로그램이나 파일 관련 주요 사항을 남기면 좋다
저작권 정보
파일과 클래스에 대한 간략한 설명
최종 수정 일자
최초 작성자
변경내역
구현한 기능의 ID
미완성 기능
발견된 버그
등등..
- 고정 양식 주석
주석을 외부 문서화 도구로 처리할 수 있도록 표준 양식에 따라 작성하는 사례
- 임의 주석
가이드 라인을 따른다
1. 주석을 달기 전에 코드를 수정해서 굳이 주석을 달 필요가 없게 만들 수 있는지 생각한다. 2. 누군가 내 코드를 읽고 있다고 생각하며 어려운 부분에 주석을 남긴다 3. 코드에 이름 남기지 않기. 소스 코드 관리 시스템에서 자동으로 관리해줌 4. API 사용하는 과정이 명확하지 않다면 참고문헌 달기 5. 코드 업데이트 할때 주석도 함께 업데이트 한다. 잘못달린 주석은 가독성을 박살냄 6. 한 함수의 주석을 여러부분으로 나눠서 작성할 때, 그 함수를 더 작은 단위의 함수로 나눌 수 없는지 검토 7. 공격적이거나 무시하는 표현 삼간다. 다른사람이 본다 8. 팀원끼리만 이해하는 농담을 적어도는 되지만 관리자의 검토 필요.. |
- 코드가 곧 문서인 코드
잘 작성된 코드는 주석이 적고 쉽게 이해할 수 있다.
함수, 매개 변수, 변수, 클래스의 역할이 잘 드러나도록 이름을 고쳐본다.
const를 활용한다.
코드를 더 작은 단위로 분해한다.
코드 분해
함수나 메서드마다 한 가지 작업만 하는 것이 바람직함
분해의 기준?
- 프린터로 뽑아볼 떄 한페이지를 넘기지 않을 만큼?
- 코드의 개략적인 형태만 봤을때 너무 빽빽하지 않을 만큼?
리펙터링을 통한 코드 분해
기존 코드에 새로운 요구사항이나 버그 픽스가 들어와 자잘하게 붙이다 보면 누더기 (크러프트)가 됨
리팩터링? - 코드의 구조를 재조정하는 작업
- 추상화 수준을 높이는 기법
ㄴ 필드 캡슐화 - 필드를 private로 설정, 게터 세터로 메서드에 접근
ㄴ 타입 일반화 - 코드를 공유하기 좋게 좀 더 일반적인 타입을 사용
- 코드를 좀 더 논리적으로 분해하는 기법
ㄴ 메서드 추출 - 거대한 메서드를 좀 더 이해하기 쉽도록 일부를 뽑아내서 새로운 메서드로 정의
ㄴ클래스 추출 - 기존 클래스에 있는 코드 중 일부를 새 클래스로 옮김
- 명칭과 위치를 개선
ㄴ 메서드, 필드 옮기기 - 좀 더 적합한 클래스나 소스 파일로 이동
ㄴ 메서드, 필드 이름 바꾸기 - 목적이 잘 드러나도록 이름을 바꾼다.
ㄴ 올리기 - OOP에서 기본 클래스로 옮김
ㄴ 내리기 - OOP에서 상속 클래스로 옮김
코드를 처음부터 누더기로 작성했거나, 갈수록 누더기가 되었다면 주기적으로 리팩터링해서 대충 작성한 코드를 제거한다.
테스팅 프레임워크를 활용하는 것이 좋다
설계 기준으로 코드 분해하기
프로그램을 구현할 때 모든 기능을 빠짐없이 코드로 작성하지 말고, 코드 분해 기법을 적용해서 나중에 모듈, 메서드, 함수에서 구현할 부분을 따로 빼놓는 방식으로 작성하면 코드의 복잡도를 낮추고 구조를 좀 더 체계적으로 만들 수 있다.
# 프로그램 설계부터 하고 코드 작성하자!
명명 규칙
- 이름의 첫 글자로 숫자가 나올 수 없다
- 더블 언더스코어(__)는 특정한 용도로 사용되기 떄문에 이름에 넣을 수 없다
- 언더스코어 다음 대문자로 시작하는것도 특수한 용도가 있어서 불가
- 글로벌 네임스페이스에서 언더스코어로 시작하는것도 용도가 있어서 불가
좋은 이름? 나쁜 이름?
가장 좋은 이름은 그 용도가 명확히 드러나는 것
타입이나 구체적인 용도와 같은 부가 정보도 이름에 담을 수 있다.
이름 지을떄마다 너무 고민할 필요는 없다. 흔히 사용하는 명명규칙만 따라도 꽤 좋은 이름을 쉽게 지을 수 있다
- 카운터
반복문등의 카운터 변수를 보통 i,j로 짓지만 이런식으로 중첩 반복문을 작성하면 i와 j를 헷갈릴 가능성이 크다.
row, col등으로 표기하면 실수할 확률이 줄어들 것이다.
- 접두어
변수 이름 앞에 그 변수의 타입이나 용도를 암시하는 문자를 붙이는 것.
편하긴 한데 시간이 갈수록 코드를 관리하기 힘들어질 수 있음
코드를 수정할때 그 뉘앙스가 담긴 변수명을 그대로 놔두면 혼란스러워질 수 있음
- 헝가리안 표기법
변수와 데이터 멤버 이름을 짓는데 사용되던 명명규칙
m과 같이 한글자로 된 접두어 대신 조금 더 정보를 담음
변수 이름을 잘 지으면 이런거 덕지덕지 붙일 필요 없음
- 게터와 세터
데이터에 접근할때는 get~(), set~()과 같은 게터 세터를 사용함
부울 타입에 접근할때는 is~() 접두어를 붙여 함수를 만드는 경우가 많음
- 대소문자 활용
변수나 데이터 멤버의 이름을 표기할 떄는 주로 소문자로 시작하고 단어 사이는 언더스코어나 대문자로 연결함.
C++에는 전통적으로 함수나 메서드의 이름을 대문자로 표기했다.
- 네임스페이스를 적용한 상수
여러곳에서 같은 이름을 나타내야할때 네임스페이스로 나눠주면 편리하다
i++를 사용할 때에는 주의
i++ + ++i
a[i] = ++i;
표준으로는 ;이 나온 후 값이 하나 증가한다고 나와있지만 구체적으로 어느 시점에서 수가 증가함을 보장하지 않음
컴파일러마다 동작이 다를 수 있음
C++17 부터는 대입연산의 오른쪽에 나온 연산을 모두 마치고 왼쪽을 편가하도록 보장하지만
그 이전에는 표준이 없으므로 컴파일러마다 동작이 다를 수 있음
상수 사용법
상수를 아무런 맥락없이 매직 넘버로 사용하지 말고 그 수가 무엇을 뜻하는지 변수명을 잘 짓자
# C++20 부터는 표준 라이브러리에 수학 상수가 추가되어 std::numbers에 자주 쓰는 상수들이 정의되어있다
포인터 대신 레퍼런스 사용하기
포인터와 레퍼런스는 다를게 없다고 생각하기 쉽지만 nullptr이 될 수 없는 레퍼런스가 더 안전하고
*&를 붙이지 않고 일반 변수처럼 사용 할 수 있기 때문에 가독성도 더 좋다
포인터를 쓰면 원본 값이 수정되는지 바로 알 수 있지만 레퍼런스를 쓰면 위로 올려다 봐야된다?
포인터도 얼마든지 수정 가능 불가능을 지정할 수 있기 때문에 어쨋든 올려봐야한다.
포인터로 값을 전달받으면 전달한 쪽 받은쪽 모두 메모리를 읽거나 수정할 수 있다
하지만 레퍼런스로 받으면 소유권이 항상 전달한 쪽에 있어서 코드를 작성하기 수월하다
사용자 정의 익셉션
C++에선 익셉션을 반드시 처리하라는 규칙도 없고 nullptr 리턴하거나 플래그 세우는식으로 얼마든지 대처할 수 있기 때문에 익셉션을 무시하기 쉽다
코드 서식
스페이스 탭 줄바꿈
팀 프로젝트를 할때는 중구난방이 되지 않도록 코드 서식을 정해둡시다
항상 프로젝트를 시작하면 이번에는 코드 스타일을 세련되게 유지하겠다고 다짐하지만 쉽지 않다
다른 사람이 짠 코드를 사용해야 할 경우 나혼자 노력한다고 될 일이 아니다
C++에서 코드 스타일에 대한 기능은 다양하지만 구체적인 적용 방법에 대한 표준이나 가이드라인은 없다
본인이 속한 조직의 방법이나 널리 사용하고 있는 방법을 따라 가독성 좋은 방법으로 코딩하도록 한다.
코딩 스타일은 프로그래밍에서 굉장히 중요한 요소이고 남의 코드를 읽는 습관을 들여 좋다고 생각한 스타일을 보고 배워 적용하려는 노력을 해야한다.
~235p