1. do-while 루프 소개
프로그램에는 최소 한 번은 실행해야 하는 동작이 있을 때가 있습니다. 시작할 때 "계속할 조건"이 처음에는 거짓일 수 있어도 말이죠. 코드에서는 메뉴를 적어도 한 번 보여줘야 하거나, 사용자에게 값을 최소 한 번 묻거나, 검사를 위해 데이터를 얻기 위해 루프 본문을 최소 한 번 실행해야 하는 상황에서 이런 경우가 발생합니다.
전형적인 문제를 상상해 보세요: "사용자에게 숫자를 물어보고 그 수가 0보다 클 때까지 반복한다". '0보다 큰지'를 확인하려면 먼저 그 숫자를 읽어야 합니다. 따라서 최소 한 번의 수행이 필수입니다: 그렇지 않으면 검사할 값 자체가 없으니, do-while은 이 시나리오를 가장 직관적으로 만들어 줍니다.
do-while의 문법과 필수 ;
do-while의 문법은 단순하지만 성격이 있습니다: 끝에 세미콜론이 필요합니다. C++에서 드문 경우 중 하나로, 컴파일러가 마치 "이상하게 보이지만 이렇게 해야 합니다"라고 말하는 것과 비슷합니다. 전체 템플릿을 통째로 외우는 것이 중요하며, 컴파일러 앞에서 조각내어 만들면 실수하기 쉽습니다. 형태는 다음과 같습니다: 먼저 do, 그 다음 블록, 그리고 while (조건) 뒤에 필수인 ;가 옵니다.
다음은 '한 번 실행하고 종료'하는 최소 예제입니다. 조건이 거짓이기 때문에 한 번만 실행됩니다:
#include <iostream>
int main() {
int x = 0;
do {
std::cout << "Hello!\n"; // Hello!
} while (x > 0);
}
핵심: 조건은 본문 이후에 검사됩니다. 따라서 본문은 적어도 한 번은 실행됩니다. 그리고 예, while (...) 뒤의 세미콜론은 필수입니다 — 없으면 컴파일 오류가 발생합니다.
while과 do-while: 한 가지 차이 — 다른 결과
while과 do-while의 차이는 작아 보일 수 있지만 결과는 매우 달라질 수 있습니다. while에서는 먼저 조건을 검사하고 그다음 본문을 실행합니다. 반면 do-while에서는 먼저 본문을 실행하고 그다음 조건을 검사합니다. 이 차이는 본문이 최소 한 번 실행되는지 여부에 영향을 주며, 종종 우리가 바로 그 동작을 필요로 합니다.
같은 값 x = 0에서 비교해 보겠습니다:
#include <iostream>
int main() {
int x = 0;
while (x > 0) {
std::cout << "while\n";
}
do {
std::cout << "do-while\n"; // do-while
} while (x > 0);
}
이걸 이야기로 읽으면, while은 "조건이 참인 동안 수행한다"라고 말합니다. 반면 do-while은 "먼저 수행하고, 그다음 계속할지 결정한다"라고 말합니다. 두 번째 방식은 첫 번째 동작 자체가 필요할 때 논리적입니다: 메뉴를 보여주거나 입력을 요청하거나 힌트를 출력하거나 "초기 시도"를 수행할 때가 그렇습니다.
2. 실제 코드에서 do-while이 유용한 경우
불필요한 장치 없이 입력과 메뉴 처리
실무에서 do-while은 보통 두 가지 상황에서 가장 많이 사용됩니다. 첫째는 "재시도 입력": 사용자에게 값을 묻고 그것이 적합한지 확인하여, 적합하지 않으면 다시 묻는 경우입니다. 둘째는 "콘솔 메뉴": 명령 목록을 보여주고 명령을 선택하게 한 뒤 동작을 수행하고, 종료를 선택할 때까지 다시 메뉴를 보여주는 경우입니다.
두 상황 모두 공통점이 있습니다 — 첫 번째 반복 없이는 아무 것도 작동하지 않습니다. 메뉴를 보여주지 않으면 선택할 것이 없고, 값을 읽지 않으면 검사할 것이 없습니다. 이는 보지 않고 수프 맛을 평가하는 것과 같습니다(물론 어떤 사람들은 그렇게 하기도 하지만, 그건 다른 이야기입니다).
예제: 우리는 > 0 인 숫자를 요구합니다
간단한 예제부터 시작합시다: n을 읽고 사용자가 n > 0 을 입력할 때까지 반복합니다. 이는 do-while의 완벽한 케이스입니다. 왜냐하면 우리는 최소 한 번의 입력이 필요하기 때문입니다.
#include <iostream>
int main() {
int n = 0;
do {
std::cout << "Enter n (> 0): ";
std::cin >> n;
} while (n <= 0);
std::cout << "n = " << n << '\n'; // 예: n = 5
}
주의할 점: 마지막의 조건은 거의 문장처럼 읽힙니다: "n이 <= 0인 동안 반복한다". do-while에서는 조건을 '계속할 조건'으로 표현하는 것이 좋은 스타일입니다. 이렇게 하면 느낌표(!)와 의미를 혼동하는 일이 줄어듭니다.
3. 예제: '저금통' 콘솔 메뉴
이제 루프 관련 주제에서 발전하는 작은 콘솔 애플리케이션의 기본을 만들어 봅시다. 오늘은 기초: 돈을 추가하고 잔액을 확인하고 잔액을 리셋하고 종료하는 '저금통'을 만들겠습니다. 배열이나 함수, 복잡한 구조는 사용하지 않습니다 — 지금은 모두 int, if/else와 루프만으로 구현합니다.
do-while로 만든 메뉴 골격
먼저 do-while 기반의 메뉴 골격을 만듭니다. 메뉴는 적어도 한 번 보여야 하므로 이 방식이 편리합니다.
#include <iostream>
int main() {
int balance = 0;
int cmd = -1;
do {
std::cout << "1 - Add\n2 - Show\n3 - Reset\n0 - Exit\n";
std::cin >> cmd;
if (cmd == 2) {
std::cout << "Balance = " << balance << '\n'; // Balance = 0
}
} while (cmd != 0);
}
아직 '꿈의 애플리케이션'은 아니지만 중요한 점이 보입니다: do-while은 "메뉴 표시 → 명령 입력 → 동작 수행 → 반복"의 구조를 아주 직접적으로 만들어 줍니다. while로도 할 수 있지만, 종종 메뉴 출력문을 중복하거나 루프 전에 준비 코드를 넣는 등의 번거로움이 필요합니다.
잔액 추가와 리셋
이제 명령 1과 3 처리 기능을 추가해 보겠습니다. 이미 배운 범위 내에서만 진행합니다: if/else와 기본 산술 연산만 사용합니다. 루프 본문을 '복잡한 덩어리'로 만들지 않는 것이 중요합니다. 논리를 단순히 유지합시다: 명령을 읽는다 → 동작을 선택한다 → 수행한다.
#include <iostream>
int main() {
int balance = 0;
int cmd = -1;
do {
std::cout << "1 - Add\n2 - Show\n3 - Reset\n0 - Exit\n";
std::cin >> cmd;
if (cmd == 1) {
int add = 0;
std::cout << "Add amount: ";
std::cin >> add;
balance = balance + add;
} else if (cmd == 2) {
std::cout << "Balance = " << balance << '\n';
} else if (cmd == 3) {
balance = 0;
std::cout << "Balance reset.\n"; // Balance reset.
}
} while (cmd != 0);
}
한 가지 중요한 점: 변수 add는 cmd가 == 1인 블록 내부에서만 존재합니다. 이는 정상이며 오히려 유용합니다: 이후에 잘못 사용될 가능성이 줄어듭니다.
알 수 없는 명령 처리
사용자가 42를 입력하면 현재 프로그램은 아무 경고 없이 단순히 메뉴를 다시 보여줍니다. 엄밀히 말해 오류는 아니지만 UX 관점에서는 커피 자판기가 당신을 무시하는 것과 비슷합니다. 간단한 알림을 추가해 보겠습니다.
#include <iostream>
int main() {
int balance = 0;
int cmd = -1;
do {
std::cout << "1 - Add\n2 - Show\n3 - Reset\n0 - Exit\n";
std::cin >> cmd;
if (cmd == 1) {
int add = 0;
std::cout << "Add amount: ";
std::cin >> add;
balance = balance + add;
} else if (cmd == 2) {
std::cout << "Balance = " << balance << '\n';
} else if (cmd == 3) {
balance = 0;
std::cout << "Balance reset.\n";
} else if (cmd != 0) {
std::cout << "Unknown command.\n"; // Unknown command.
}
} while (cmd != 0);
}
조건 else if (cmd != 0)는 깔끔하게 보입니다: 종료 명령에는 아무런 불평을 하지 않습니다. 이런 작은 점들이 프로그램을 '살아 있는' 느낌으로 만들어 줍니다.
4. 실행 순서와 자주 빠지는 함정
do-while의 실행 흐름
초보자가 루프를 헷갈려 할 때 대부분 문제는 문법이 아니라 실행 순서에 있습니다. 그래서 흐름도를 한 번 보는 것이 유익합니다. 읽는 방법은 간단합니다: 먼저 본문을 실행하고, 그다음 조건을 검사하여 반복할지 아니면 종료할지 결정합니다.
flowchart TD
A[do-while 진입] --> B[루프 본문 실행]
B --> C{조건이 참인가?}
C -->|예| B
C -->|아니오| D[루프 종료]
이 흐름도를 기억하면 "조건이 거짓인데 한 번 실행된 이유" 같은 대부분의 질문이 사라집니다. do-while에서는 그 동작이 버그가 아니라 의도입니다.
루프는 종료를 향해 나아가야 한다
do-while도 모든 루프의 기본 규칙을 면제해 주지 않습니다: 루프는 언젠가 끝날 이유가 있어야 합니다. 어떤 경우에는 변수 변화가 이유가 되고, 어떤 경우에는 사용자 입력이 이유가 되며, 어떤 경우에는 반복마다 나누어 값이 줄어드는 식일 수 있습니다. 중요한 점은 '종료로 향하는 이유'가 있어야 한다는 것입니다.
우리 메뉴에서는 종료 이유가 단순합니다: 사용자가 0을 입력하면 조건 cmd != 0이 거짓이 됩니다. 만약 본문에서 cmd를 읽는 것을 깜빡하거나(또는 실수로 다른 값으로 덮어쓰면) 무한 루프가 될 수 있습니다. 무한 루프가 항상 '나쁜' 것은 아니지만, 여기서는 정상적으로 종료되는 루프가 주제이므로 각 반복이 종료로 어떻게 다가가는지 스스로 물어보는 습관을 가지십시오: "한 회전이 끝난 후 무엇이 바뀌는가? 왜 이것이 종료에 가깝게 만드는가?"
세미콜론과 빈 본문
do-while에는 두 가지 '고전적' 함정이 있습니다. 첫째는 ;를 while 뒤에 깜빡하는 경우입니다. 둘째는 do { ... } 뒤에 우연히 ;를 넣어 루프를 빈 본문으로 만들어 버리는 경우입니다. 그러면 내부에서 무슨 일이 일어나리라 생각하지만 실제로는 아무 것도 일어나지 않습니다.
시각적으로 올바른 템플릿을 외워 두는 것이 좋습니다:
do {
// 루프 본문
} while (조건);
언젠가부터 } while (...) ;를 하나의 '결합'으로 인식하면 실수가 훨씬 줄어듭니다.
5. do-while 사용 시 전형적인 실수
실수 №1: while (조건) 뒤의 세미콜론을 잊음.
이 실수는 특히 속상한데, 뇌는 "거의 while과 같잖아!"라고 외치지만 컴파일러는 "아니야"라고 응답합니다. 해결법은 간단합니다: do-while을 완전한 템플릿으로 외우세요 — do { ... } while (...); — 그리고 학습 프로젝트에서는 본문이 한 줄이라도 중괄호를 항상 사용하세요.
실수 №2: 반복 조건과 종료 조건을 혼동함.
종종 '나갈 조건'을 써서 while (...)에 넣어 루프가 반대로 동작하게 만드는 경우가 있습니다. 좋은 습관은 조건을 큰 소리로 '계속할 동안 …'으로 읽어 보는 것입니다. 문장이 자연스럽지 않으면 당신은 아마도 '종료 조건'을 쓴 것입니다.
실수 №3: 본문이 상황을 변화시키지 않아 무한 루프가 됨.
메뉴에서는 std::cin >> cmd;를 깜빡하거나, 다른 변수에 읽어오거나, 항상 같은 값을 대입하는 경우가 있습니다. 입력 루프에서는 조건에 사용되는 변수가 어디에서도 변경되지 않으면 무한 루프가 됩니다. 실행 전에 스스로 물어보세요: "한 번의 반복에서 무엇이 바뀌는가? 그것이 왜 종료에 다가가게 하는가?"
실수 №4: 하나의 장소에 너무 많은 로직을 넣어 루프가 읽기 어려워짐.
do-while은 메뉴에 자주 쓰이는데, 메뉴는 쉽게 '콤바인'이 되어 버립니다: 명령이 열 개, if 문이 스무 개가 되면 파일을 여는 것조차 두려워집니다. 아직 함수 단원을 지나지 않았다 해도 규율을 유지할 수 있습니다: 본문은 항상 같은 순서로 하나의 일을 하게 하세요 — 힌트 표시 → 명령 읽기 → 해당 분기 수행 → 처음으로 돌아가기.
실수 №5: 잘못된 위치의 세미콜론 때문에 우연히 빈 루프를 만듦.
가끔은 do { ... }; while (cond); 같이 쓰거나 다른 조합을 만들어 프로그램이 컴파일은 되지만 동작이 이상해지는 경우가 있습니다. 동작이 마법처럼 보인다면 대개 원인은 문장 부호입니다: ;와 {}는 C++에서 장식이 아니라 제어 요소입니다 — 전기 배선처럼 다루세요: 불필요한 선 하나가 논리를 망가뜨릴 수 있습니다.
GO TO FULL VERSION