눈에 잘 들어오는 코드는 이해하기 쉬우며, 이는 장기적으로 유지보수성과 협업 효율성을 높이는 핵심 요소다. 단순한 기능 구현을 넘어서, 코드의 시각적 배치와 일관성은 개발자의 인지 부담을 줄이고 오류를 조기에 발견할 수 있도록 도와준다.
줄 바꿈 최적화로 가독성 향상
아래 예제는 초기에 각 파라미터에 주석을 달아 설명하려 한 의도는 좋지만, 세로 공간을 과도하게 차지하며 중복된 주석이 반복되어 시각적 노이즈가 된다.
public class PerformanceTester {
public static final TcpConnectionSimulator wifi =
new TcpConnectionSimulator(
500, /* Kbps */
80, /* millisecs latency */
200, /* jitter */
1 /* packet loss % */);
public static final TcpConnectionSimulator t3_fiber =
new TcpConnectionSimulator(
45000, /* Kbps */
10, /* millisecs latency */
0, /* jitter */
0 /* packet loss % */);
}
반면, 전체 파라미터 의미를 상단 주석으로 정리하고, 생성자 호출을 한 줄에 기술하면 훨씬 간결해진다.
public class PerformanceTester {
// Connection: (bandwidth_Kbps, latency_ms, jitter_ms, loss_percent)
public static final TcpConnectionSimulator wifi =
new TcpConnectionSimulator(500, 80, 200, 1);
public static final TcpConnectionSimulator t3_fiber =
new TcpConnectionSimulator(45000, 10, 0, 0);
}
불규칙한 로직은 함수로 추상화
다음과 같은 반복적인 테스트 코드는 구조가 고르지 못하고 중복이 많다.
assert(ExpandFullName(db_conn, "Doug Adams", &err) == "Mr. Douglas Adams");
assert(err == "");
assert(ExpandFullName(db_conn, " Jake Brown ", &err) == "Mr. Jake Brown III");
assert(err == "");
공통 패턴을 별도 함수로 분리하면, 테스트 케이스 자체가 명세처럼 읽힌다.
void VerifyNameExpansion(string input, string expectedName, string expectedError) {
string error;
string result = ExpandFullName(database_connection, input, &error);
assert(result == expectedName);
assert(error == expectedError);
}
VerifyNameExpansion("Doug Adams", "Mr. Douglas Adams", "");
VerifyNameExpansion(" Jake Brown ", "Mr. Jake Brown III", "");
이 방식은 코드 중복 제거뿐 아니라 새로운 테스트 추가도 용이하게 만든다.
열 정렬을 통한 정보 그룹화
함수 호출 내 인자가 여러 줄에 걸쳐 나열될 경우, 열 기준으로 정렬하면 값의 대응 관계를 직관적으로 파악할 수 있다.
VerifyNameExpansion("Doug Adams" , "Mr. Douglas Adams" , "");
VerifyNameExpansion(" Jake Brown ", "Mr. Jake Brown III", "");
VerifyNameExpansion("No Such Guy" , "" , "no match found");
유사하게, C 스타일 구조체 배열에서도 정렬은 필드 간 관계를 명확히 한다.
struct Command {
const char* name;
void* flag;
CmdHandler handler;
};
Command commands[] = {
{ "timeout", NULL, cmd_spec_timeout },
{ "timestamping", &opt.timestamping, cmd_boolean },
{ "tries", &opt.ntry, cmd_number_inf },
{ "useproxy", &opt.use_proxy, cmd_boolean },
{ "useragent", NULL, cmd_spec_useragent },
};
논리적 블록 단위로 선언 그룹화
클래스 내 메서드나 변수는 역할에 따라 섹션으로 나누면 탐색이 쉬워진다. 각 섹션은 주석으로 목적을 명시한다.
class FrontendServer {
public:
// 사용자 프로필 관련 요청 처리
void ViewProfile(HttpRequest* request);
void SaveProfile(HttpRequest* request);
// HTTP 응답 유틸리티
string ExtractQueryParam(HttpRequest* request, string param);
void ReplyOK(HttpRequest* request, string html);
void ReplyNotFound(HttpRequest* request, string error);
// 데이터베이스 연결 지원
void OpenDatabase(string location, string user);
void CloseDatabase(string location);
};
단락별 주석으로 흐름 강조
함수 내부 코드도 자연어 문단처럼 구분하면, 독자는 각 블록의 의도를 빠르게 파악할 수 있다.
def suggest_new_friends(user, email_password):
# 1. 친구 목록에서 이메일 수집
friends = user.friends()
friend_emails = {f.email for f in friends}
# 2. 이메일 계정 연락처 가져오기
contacts = import_contacts(user.email, email_password)
contact_emails = {c.email for c in contacts}
# 3. 친구가 아닌 연락처 매칭
candidates = contact_emails - friend_emails
suggestions = User.objects.filter(email__in=candidates)
# 4. 결과 페이지에 전달
display.update({
'user': user,
'friends': friends,
'suggested_friends': suggestions
})
return render("suggestions.html", display)
스타일보다 일관성이 우선
코드 스타일은 팀 내에서 일관되게 유지하는 것이 중요하다. 다음 두 가지 클래스 선언 스타일 모두 유효하지만, 하나를 선택하고 지속해야 한다.
class Logger {
// ...
};
// 또는
class Logger
{
// ...
};
개인 취향보다는 프로젝트 전체의 형식 통일이 더 큰 가치를 지닌다. 자동 포매터(linter, formatter) 도입은 이러한 일관성을 보장하는 데 효과적이다.