내가 주로 주석을 쓰던 때를 생각해보면 나쁜 디자인의 코드를 숨기고 설명을 하려고 주로 썼던 것 같다. 하지만 계속 업데이트가 되고 코드가 바뀌면서 주석의 신뢰성은 점점 낮아졌고 주석을 보지 않게 되었다. 결국 주석도 레거시가 되어 나쁜 코드를 더 나쁘게 만드는 상황이 된 것이다.
주석은 나쁜 코드를 막아주지 못한다.
나쁜 코드를 설명하기 위해 주석으로 설명하다 보면 점점 지저분해지는 코드를 볼 수 있다. 많은 개발자들이 가장 깔끔한 코드는 주석없이 의도를 잘 설명한 코드가 깨끗한 코드라고 한다. 그러니 우리는 주석에 시간을 투자하는 것보다 아래처럼 투자해보는 것은 어떨까?
- 주석을 달아야 코드를 이해할 수 있다면 다시 작성해보자
- 코드로 의도를 표현할 수 있게 고민하고 설계한다.
- 주석보단 좋은 네이밍을 통해 설명하자.
좋은 주석
그럼에도 코드가 모든 의도를 표현하는 것은 불가능에 가깝다. 그럴 때 사용하는 것이 주석이다. 주석을 작성하면서 이게 나쁜 주석인 것은 아는 경우가 많았지만 좋은 주석은 어떻게 써야 하는지 잘 모르는 경우가 많았다. 이번 기회를 통해 정리해두고 주석이 필요한 상황을 잘 생각해보면 좋을 것 같다.
법적인 주석
소프트웨어를 개발하다면 법적인 부분을 생각하여 처리해야 할 필요가 있다. 코드만으로는 이러한 법까지 의도를 표현하기에 부족하다. 그렇기 때문에 이럴 때는 주석으로 남겨주는 것이 좋다. 하지만 법또한 바뀌기 때문에 링크나 라이선스 정보를 달아두는 것이 더 좋다.
// SPDX-FileCopyrightText: © 2020 Matija Šuklje <matija@suklje.name>
// SPDX-License-Identifier: BSD-3-Clause
정보 제공
아까 전에는 코드로 의도를 표현하라고 했는데 정보를 주석으로 제공하라는게 무슨 의미인지 싶은데 코드로 표현할 수 없을 때를 의미한다. 아래의 코드를 보면 단번에 이해할 수 있을 것이다.
// 매일 새벽 5시 15분 마다 동작
@Scheduled(cron = "0 15 5 * * *")
// 영어 및 숫자를 허용하며, 숫자키와 관련된 특수문자만 허용한다
String passwordRegEx = "^[a-zA-Z\\d`~!@#$%^&*()-_=+]{8,24}$";
코드로 의도를 설명할 수는 있지만 모든 정보까지는 표현하기 어려울 때 정보를 제공하는 주석을 작성한다
의도를 표현하는 주석
코드로 의도를 표현하라고 했지만 주석으로 의도를 표현하라는 것이 무슨 의미인지 잘 이해가 가지 않았다. 예를 들어 특정한 시나리오에 대한 테스트를 시도하는 코드나 하나의 함수 안에 복잡한 처리를 해야 될 경우 사용해야 될 것 같다고 생각한다. 예를 들어 배치 프로그램이나 스케줄러 같은 경우에 사용되지 않을까? 생각된다.
// 스레드를 대량 생성하는 방법으로 어떻게든 경쟁 조건을 만들려 시도한다.
for (int i = 0; i < 25000; i++) {
WidgetBuilderThread widgetBuilderThread =
new WidgetBuilderThread(widgetBuilder, text, parent, failFlag);
Thread thread = new Thread(widgetBuilderThread);
thread.start();
}
의미를 명확하게 알려주는 주석
공식 라이브러리나 모듈을 사용할 때 그 의미가 무엇인지 파악하기 힘든 경우가 종종 생긴다. 그렇다고 공식 라이브러리와 모듈을 수정한다면 예상치 못한 버그와 취약점이 생길 수 있기 때문에 쉽사리 건들지 못한다. 그럴땐 어쩔 수 없이 주석을 통해 더 명확하게 설명한다면 좋은 주석이 될 것이다.
assertTrue(a.compareTo(a) == 0); // a == a
assertTrue(a.compareTo(b) != 0); // a != b
하지만 나쁜 주석이 될 가능성도 크기 때문에 항상 주의하여 작성해야 한다.
결과를 경고하는 주석
코드로 의도를 표현할 순 있지만 의도로 인한 예상치 못한 상황은 설명하기 어렵다. 이런 상황에 결과를 경고해주는 코드를 작성한다면 좋을 수 있을 것 같다.
- 작동이 오래걸리는 코드
- 특정한 환경에서 실행되면 오류를 일으킬 수 있는 경우
- 코드를 동작시키기 전에 사전에 설정을 해줘야 되는 경우
TODO 주석
테스트를 구현할 때 비어있는 구현체를 만들어 작업하는 경우가 종종 생긴다. 그런 상황에 TODO라고 주석을 달아놓고 다음날에 작업을 하게 된다면 시작하는 부분을 알려주는 아주 좋은 주석이 될 것이다.
// TODO: 체크인 기능을 구현하여 현재 숙박한 고객 유저 변경하는 모듈
public void checkIn() {
}
중요성을 강조하는 주석
코드를 작성하다 보면 필수적으로 처리해줘야 하는데 메서드로 분리시키기 매우 애매한 경우가 있다. 예를 들어 아이디와 패스워드를 입력 받을 때 trim()을 통해 양쪽 공백을 제거해주는 것이 대표적인 예로 볼 수 있다.
public User login (String email, String password) {
// 공백을 제거하지 않고 처리할 경우 데이터의 무결성을 해칠 수 있음
email.trim();
password.trim();
'''
}
나쁜 주석
대다수 주석이 나쁜 주석에 속하고 나쁜 주석의 공통점은 나쁜 디자인의 코드를 보완하기 위하는데 주로 사용되고 설명서라기 보단 개인의 독백에 가까운 역활을 한다.
혼잣말 같은 주석
우리는 의무감으로 주석을 작성하는 경우가 종종 있다. 예를 들어 파일을 읽어올 때 IOExceptoin을 달아야 할 때나 UI에서는 20X를 제외한 다른 응답 코드를 받고 예외를 처리하고 어떻게 처리를 하는지 설명하기 위해서 작성을 한다. 하지만 이런 주석들을 혼잣말 처럼 적어놓는다면 우리는 결국 몇 바이트의 주석으로 인해 코드에 대한 신뢰도 잃고 시간도 낭비하게 된다.
public void loadProperties() {
try {
String propertiesPath = propertiesLocation + "/" + PROPERTIES_FILE;
FileInputStream propertiesStream = new FileInputStream(propertiesPath);
loadedProperties.load(propertiesStream);
} catch (IOException e) {
// 속성 파일이 없다면 기본값을 모두 메모리로 읽어 들였다는 의미다.
}
}
주석을 읽고도 코드를 뜯어봐야 한다면 의미가 없는 주석이다.
반복되는 주석
코드로 충분한 의도를 표현했음에도 불구하고 설명하는 주석이다. 이러한 주석은 우리의 시간을 낭비하게 만들 뿐이다.
// 생명주기 이벤트 모듈
protected LifeCycle = new LifeCycle;
//프로세서 지연 값
protected int backgroundProcessorDelay = -1
// this.closed가 true일 때 반환되는 유틸리티 메서드다.
// 타임아웃에 도달하면 예외를 던진다.
public synchronized void waitForClose(final long timeoutMillis) throws Exception {
if (!closed) {
wait(timeoutMillis);
if (!closed) {
throw new Exception("MockResponseSender could not be closed");
}
}
}
오해가 생길 수 있는 주석
같은 말을 하더라도 받아들이는 입장에 따라서 우리는 다르게 받아들이는 경우가 있다. 주석 또한 다르지 않기 때문에 오해가 생길 수 있는 주석은 달지 않는 것이 좋다.
// this.closed가 true일 때 반환되는 유틸리티 메서드다.
// 타임아웃에 도달하면 예외를 던진다.
public synchronized void waitForClose(final long timeoutMillis) throws Exception {
if (!closed) {
wait(timeoutMillis);
if (!closed) {
throw new Exception("MockResponseSender could not be closed");
}
}
}
true가 되는 경우 기달렸다가 뭘하는 거지? 알 수 없기 때문에 우리는 결국 코드를 추적해야 한다.
의무적으로 다는 주석
모든 변수에 주석을 달아야 한다고 생각하는 것은 일을 위한 일을 할 뿐이다. 코드를 오히려 복잡하게 만들고 거짓된 정보로 혼란을 야기하는 경우가 많다. 예를 들어 모든 변수를 설명하는 경우가 그렇다.
/**
*
* @param title CD 제목
* @param author CD 저자
* @param tracks CD 트랙 숫자
* @param durationInMinutes CD 길이(단위: 분)
*/
public void addCD(String title, String author, int tracks, int durationInMinutes) {
CD cd = new CD();
cd.title = title;
cd.author = author;
cd.tracks = tracks;
cd.duration = durationInMinutes;
cdList.add(cd);
}
코드의 이력을 기록하는 주석
코드에 이력을 적은 주석을 전에 한번 본 적이 있었는데 처음에는 최신화를 열심히 하여 잘 작성해뒀지만 점점 요구사항이 바뀜에 따라ㄱ 결국 레거시가 되어 의미없는 주석이 되어버리는 경우가 많았다. 이처럼 코드로 표현할 수 있는 것을 주석으로 표현하다보면 의미없는 주석이 되어 버리고 만다. 의미없는 주석은 잘 작성한 주석에도 영향을 끼치기 때문에 이런 나쁜 주석은 조심해야한다.
* 변경 이력 (11-Oct-2001부터)
* ------------------------------------------------
* 11-Oct-2001 : 클래스를 다시 정리하고 새로운 패키징
* 05-Nov-2001: getDescription() 메소드 추가
주석이 된 코드
제일 많이 보게 된 주석 중 하나다. 아무런 설명이 없는 주석이 되어 있는 코드를 보면 지워야 겠다는 생각 보단 필요한 이유가 있어서 남겨 놓았겠지? 라고 생각을 한다. 결국 주석은 시스템이 전체적으로 리팩토링할 때 까지 우리를 귀찮게 굴고 기존 코드의 형태까지 나쁜 영향을 끼친다. 우리는 깃이라는 좋은 코드 관리 시스템이 있기 때문에 이제는 삭제 해도 괜찮다.
InputStreamResponse response = new InputStreamResponse()
;response.setBody(formatter.getResultStream(), formatter.getByteCount());
// InputStream resultStream = formatter.getResultStream();
// StreamReader reader = new StreamReader(resultsStream);
// response.setContent(reader.read(formatter.getByteCount()));
그외 나쁜 주석
- 작성한 저자나 공로를 돌리는 행위의 주석
- html을 주석으로 만든 코드
- TMI 주석 (너무 많은 정보를 담은 주석)
'도서' 카테고리의 다른 글
클린코드 (함수) (0) | 2022.07.04 |
---|---|
클린 코드 (의미 있는 이름) (0) | 2022.06.26 |
클린 코드에 대하여 (0) | 2022.06.21 |
테스트 주도 개발 패턴 (2) (0) | 2022.05.29 |
테스트 주도 개발 패턴 (0) | 2022.05.15 |