오늘 튜터님께 코드 리뷰를 받았다. 다른 부분은 다 괜찮지만 굳이 뽑자면 while (true)에서 true 값 대신 플래그 변수를 사용하는게 어떻냐는 말을 들었다.
나도 예전에 while (true)가 좋지 않은 코딩 스타일이라고 들은 적이 있었고, 실제로 과제의 초창기 코드 버전에서는 true 대신 플래그 변수를 사용했었다. 하지만 간단한 프로젝트여서 루프 종료 조건을 쉽게 파악할 수 있고, 불필요한 변수와 코드 덩어리들이 생겨서 while (true)로 다시 바꿨던 것인데...
일단 튜터님이 지적하신 사항이니 이게 왜 안 좋은 것인지, 절대 쓰면 안 되는지에 대해 알아봤다.
종료 조건을 특정 시점에 여러 개 넣어야 하는 경우, while (true) + break가 더 가독성이 좋을 수 있다.
while (!command.equals("exit") && !command.equals("restart")) {
// do something
}
위 코드보다는
while (true) {
String command = scanner.nextLine();
if (command.equals("exit")) break;
if (command.equals("restart")) restart();
executeCommand(command);
}
이 코드가 이해하기 더 쉽다.
break, return)가 포함되어 있으면 괜찮다while (true) {
processNextTask();
if (isShutdownRequested()) {
break;
}
}
while (true) 내부에 break, return, throw 등이 확실하게 존재하면 가독성 문제가 적다. 위 코드에서 종료 조건이 break로 확실히 관리되므로, while (condition)으로 바꿀 필요가 없을 수도 있다.
GUI 이벤트 루프, 네트워크 서버 등 무한루프를 돌면서 특정 이벤트가 발생할 때마다 동작을 해야하는 경우가 있다. 아니면 크기가 얼마나 되는지 모르는 파일을 한 줄씩 읽을 때에도 사용된다.
가장 많이 언급되는 내용이다.
while (true)는 반복의 종료 조건이 코드 내부에 숨어 있기 때문에 흐름을 이해하기 어렵다. 반면 while (condition) 또는 while (!condition)은 헤더만 봐도 종료 조건을 이해할 수 있다.
while (true) {
if (...) {
while (...) {
if () {
// ...
break;
}
}
break;
} else if (...) {
// ...
break;
} else {
// ...
}
}
만약 루프가 중첩으로 있게 된다면 코드에서 break를 검색해서 종료 조건을 찾더라도 이 break가 어떤 루프에 적용되는 것인지 한 눈에 파악하기 어려울 수 있다.
(C 언어에서는 이런 복잡한 중첩 루프에서 좀 더 편하게 코드 흐름을 제어할 수 있도록 GOTO를 사용했었지만, 남용하는 경우가 많아지면서 잘 안 쓰게 되었다. 일부 개발자들은 이 break가 GOTO 같이 동작하므로 좋지 않은 습관이라고 말한다.)
종료 조건이 루프 내부 여러 곳에 흩어져 있으면, 후에 로직을 수정할 때 실수할 가능성이 커진다. 새로운 조건을 추가할 경우 코드가 예상치 못한 방식으로 동작할 수도 있다.
int x = someFunc();
while (true) {
if (x == 1) {
// ...
break;
} else if (x == 2) {
// ...
break;
}
// more else if
}
위 코드에서는 x의 값에 따라 분기 처리를 하고 무한루프를 빠져나가도록 하고 있다.
하지만 여기서 개발자가 else 구문을 빠뜨렸다면? 아니면 협업 개발자가 실수로 지웠다면? sleep이나 wait 등의 동작이 없기 때문에 딜레이 없이 루프를 돌며 불필요하게 CPU를 점유하게 될 것이다. 이는 곧 소프트웨어의 성능 문제로 이어진다.
즉, 구조적으로 방어적이지 않은 프로그래밍 방식이라는 것이다.
일부 코드는 while (true)가 아니라 for문, do ~ while 등을 사용하는게 더 나은 경우도 있다.
Basically don’t take anything as gospel. Whether it’s “use tdd”, “never use if/else statements!”, “always stick to SOLID principles”.
It all depends on the situation.
The real goal is to make software that’s maintainable and as easy to understand as possible. If rewriting a module just so that it adheres to SOLID principle makes your program less understandable and harder to maintain then don’t do it.
"진짜 목표는 유지보수하기 쉽고, 최대한 이해하기 쉬운 소프트웨어를 만드는 것이다."
(The real goal is to make software that’s maintainable and as easy to understand as possible.)
처음 조사할 때 "while (true)를 쓰면 안 좋다!"라는 이야기가 주류일 줄 알았는데, 커뮤니티에서 논쟁이 많이 이루어지고 있어서 놀랐다. 양쪽 의견 다 일리가 있다고 생각한다.
다만 코드의 일관성을 어느 정도 유지할 수 있는 개인 프로젝트와는 달리, 협업에서는 언제든지 원래 의도와는 다르게 로직이 수정될 수 있다. 때문에 안정적인 유지보수를 위해 while (true)는 지양하는 것이 좋을 것 같다.