Java 15
for 문으로 정해진 횟수만큼 반복하기: 실행 순서 해부하기
초기식, 조건식, 변화식이 괄호 안에 한꺼번에 있어 반복이 어떤 순서로 진행되는지 놓치기 쉽다.
근거 · 교안 p109-p114
1장: 반복, 왜 필요할까? 지겨운 반복의 시작
솔라는 의자에 등을 기댄 채 화면을 물끄러미 바라보고 있었다. 모니터에는 방금 막 완성한 코드 몇 줄이 떠 있었다. 대단한 프로그램은 아니었다. 그저 화면에 환영 인사를 다섯 번 출력하는 게 전부였다.
print("안녕하세요!")
print("안녕하세요!")
print("안녕하세요!")
print("안녕하세요!")
print("안녕하세요!")
결과 창에는 ‘안녕하세요!’라는 문장이 깔끔하게 다섯 줄 찍혀 나왔다. 만족스러웠다. 솔라는 키보드에서 손을 떼며 중얼거렸다.
“똑같은 일을 여러 번 시키려면, 그냥 이렇게 복사해서 붙여넣으면 되는 거 아닌가?”
간단하고 확실한 방법이었다. 다섯 번이든 열 번이든, 필요한 만큼 복사해서 붙여넣으면 그만이었다. 솔라는 자신의 발견이 꽤 그럴싸하다고 생각하며 어깨를 으쓱했다. 그때, 방문이 조용히 열리고 언니 루나가 들어왔다.
“뭘 그렇게 열심히 보고 있어?”
루나는 솔라의 의자 뒤로 다가와 모니터 화면을 들여다봤다. 똑같은 코드 다섯 줄이 나란히 있는 것을 보고도 별다른 말을 하지 않았다. 그저 조용히 화면을 가리킬 뿐이었다.
“다섯 번이네.”
“응. 다섯 번 인사하는 거 만들었어. 그냥 복사 붙여넣기 하니까 금방 되던데?”
솔라는 자신의 방법을 뽐내듯 말했다. 루나는 고개를 끄덕이더니, 엉뚱한 질문을 던졌다.
“만약 백 번 인사해야 한다면?”
“백 번?”
솔라는 잠시 말을 잃었다. 백 번. 마우스 휠을 한참 내려야만 끝이 보일 정도로 화면을 가득 채운 print("안녕하세요!")의 행렬이 머릿속에 그려졌다. 상상만으로도 벌써부터 번거로웠다.
“그건… 좀 많긴 하네. 그래도 시간만 들이면….”
솔라의 자신감 넘치는 목소리가 살짝 잦아들었다. 루나는 책상 위에 놓여 있던 메모지와 펜을 솔라 쪽으로 밀어주었다.
“코딩 말고, 손으로 한번 해볼까? 이 종이에 ‘안녕하세요’라고 딱 열 번만 써 봐.”
솔라는 펜을 집어 들었다. 처음에는 별거 아니라는 듯 쾌활하게 써 내려갔다.
안녕하세요.
안녕하세요.
안녕하세요.
하지만 네 번째, 다섯 번째 문장을 쓸 때부터 글씨가 조금씩 흔들리기 시작했다. 똑같은 글자를 기계처럼 반복해서 쓰는 일은 생각보다 지루했다. 일곱 번째 문장을 쓸 때쯤엔 자기도 모르게 작은 한숨이 터져 나왔다.
“으… 생각보다 귀찮네.”
겨우 열 번을 채운 솔라가 펜을 내려놓았다. 루나는 솔라가 쓴 메모지를 가리켰다.
“이제 세 번째 ‘안녕하세요’를 ‘반가워요’로 바꿔볼래?”
솔라는 목록을 훑어 세 번째 줄을 찾아냈다. 원래 있던 글자 위에 두 줄을 긋고, 그 옆에 ‘반가워요’라고 고쳐 적었다. “됐어. 이건 쉽네.”
“그럼, 만약 백 번을 썼다고 상상해 봐. 그중에서 쉰일곱 번째 문장을 찾아서 바꾸려면?”
“쉰일곱 번째…?”
솔라는 눈으로 직접 한 줄 한 줄 세어 내려가는 자신의 모습을 상상했다. 하나, 둘, 셋… 쉰일곱 번째 줄을 찾는 것부터가 일이었다. 실수로 한 줄을 건너뛰거나 두 번 세기라도 하면 처음부터 다시 세어야 할 판이었다.
루나는 거기서 멈추지 않고 한 걸음 더 나아갔다.
“만약 ‘안녕하세요’ 뒤에 느낌표를 하나씩 더 붙여서, 전부 ‘안녕하세요!!’로 바꿔야 한다면 어떡할 거야? 백 개 전부 다.”
솔라의 미간이 완전히 찌푸려졌다. 하나를 고치는 것도 성가신데, 백 개를 전부 다 고쳐야 한다니. 손으로 쓴 것이라면 백 번을 지우고 다시 써야 하고, 컴퓨터 코드라고 해도 백 줄을 일일이 찾아서 수정해야 했다.
“아… 그걸 전부 다 일일이 고쳐야 한다고? 말도 안 돼.”
그 순간, 솔라는 깨달았다. 자신이 똑똑하다고 생각했던 ‘복사 붙여넣기’ 방법이 얼마나 어리석고 위험한 생각이었는지를. 당장은 편해 보일지 몰라도, 나중에 무언가 바꿔야 할 때 거대한 재앙이 되어 돌아올 터였다. 다섯 줄일 때는 문제가 아니었지만, 백 줄, 천 줄이 된다면 끔찍한 일이 될 것이다.
솔라는 자신의 모니터에 떠 있는 다섯 줄의 코드를 다시 쳐다봤다. 조금 전까지 만족스럽게 보였던 코드가 이제는 시한폭탄처럼 느껴졌다.
“알겠어. 언니 말이 맞아. 그냥 복사해서 붙여넣는 건 나중에 수정하기가 너무 힘들어. 특히 개수가 많아지면… 이건 정말 비효율적인 방법이야.”
번거로움. 솔라는 반복 작업의 본질을 꿰뚫는 한 단어를 떠올렸다. 루나는 솔라의 깨달음을 조용히 지켜보다가, 고개를 끄덕였다.
“응. 그래서 사람들은 컴퓨터한테 시키는 방법을 생각해냈어. ‘이거, 정해진 횟수만큼 네가 알아서 반복해 줘’ 하고.”
솔라의 눈이 호기심으로 반짝였다. 방금 전까지 느꼈던 막막함과 번거로움이 단번에 해결될 것 같은 예감이 들었다. 솔라는 의자를 앞으로 당겨 앉으며 물었다.
“그런 방법이 있어? 어떻게 하는 건데?”
2장: for 문의 네 가지 핵심 구성 요소
루나는 솔라의 질문에 말없이 미소만 지었다. 그리고는 의자를 돌려 다시 키보드에 손을 얹었다. 솔라가 잔뜩 기대에 찬 눈으로 지켜보는 가운데, 루나는 기존의 print 다섯 줄을 지우고 완전히 새로운 형태의 코드를 작성하기 시작했다.
for (int i = 0; i < 5; i++) {
print("안녕하세요!");
}
솔라는 화면에 나타난 낯선 기호들의 조합을 보고 미간을 찌푸렸다. for? 괄호 안에는 세미콜론으로 나뉜 알 수 없는 식들이 들어 있었다. 간결해지긴 했지만, 한눈에 이해하기는 훨씬 더 어려워 보였다. 이전의 if 문을 처음 봤을 때처럼, 여러 규칙이 한 줄에 뭉쳐 있는 느낌이었다.
“언니, 이게 바로 그 방법이야? 이게 어떻게 ‘안녕하세요’를 다섯 번 출력한다는 거야? 괄호 안에 뭐가 이렇게 많아? 그리고 i는 또 뭐고… 이게 더 복잡해 보여.”
솔라의 목소리에는 실망감이 묻어났다. 복사 붙여넣기의 번거로움을 해결해 줄 마법 같은 방법일 줄 알았는데, 눈앞에 나타난 것은 해독해야 할 암호문에 가까웠다.
루나는 키보드에서 손을 떼고, 아까 솔라가 썼던 메모지의 뒷면을 집어 들었다. 그리곤 펜으로 커다란 사각형을 그리고 그 안을 세 개의 선으로 나눠 네 칸으로 만들었다.
“복잡해 보이지? 그건 여러 가지 약속이 한곳에 모여 있어서 그래. 한번 분해해 보자.”
루나는 첫 번째 칸 위에 ‘① 시작’이라고 적었다. 그리고는 코드의 int i = 0; 부분을 가리켰다.
“이건 반복을 위한 준비야. ‘i라는 이름의 카운터를 0부터 시작할게’라고 선언하는 거지. 딱 한 번만 실행돼. 반복의 출발점을 정하는 거야.”
솔라는 ‘i’가 일종의 횟수를 세는 변수라는 것을 어렴풋이 짐작했다. 루나는 이어서 두 번째 칸 위에 ‘② 조건’이라고 적고, i < 5; 부분을 그 칸 안에 옮겨 적었다.
“두 번째는 반복을 계속할지 말지 결정하는 조건이야. ‘카운터 i가 아직 5보다 작니?’라고 매번 물어보는 거지. 이 질문에 ‘응, 맞아’라고 대답할 수 있을 때만 다음 일을 해.”
“질문?”
“응. 문지기 같은 거야. 통과할 수 있는지 없는지 검사하는 거지.”
루나는 세 번째 칸에 ‘④ 변화’라고 적었다. 조금 전과 순서가 다른 ‘④’라는 숫자에 솔라가 고개를 갸웃했지만, 루나는 일단 코드의 i++ 부분을 가리키며 설명을 이었다.
“이건 한 번 반복이 끝날 때마다 카운터를 어떻게 바꿀지에 대한 규칙이야. i++는 ‘i를 1만큼 증가시켜라’는 뜻이지. 우리가 ‘안녕하세요’를 한 번 쓰고 나서 다음번을 위해 마음속으로 숫자를 하나 세는 것과 같아.”
마지막으로 루나는 네 번째 칸에 ‘③ 할 일’이라고 적었다. 그리고 중괄호 {} 안에 있던 print("안녕하세요!");를 가리켰다.
“그리고 이게 바로 우리가 진짜로 반복하고 싶었던 작업, 즉 ‘할 일’이야. 조건이 맞으면 이걸 실행하는 거지.”
솔라는 루나가 완성한 네 개의 칸을 물끄러미 내려다보았다.
| ① 시작 (초기식) | ② 조건 (조건식) | ③ 할 일 (종속 문장) | ④ 변화 (변화식) |
|---|---|---|---|
int i = 0; | i < 5 | print("안녕하세요!"); | i++ |
한 줄에 뒤섞여 있던 암호문 같던 코드가 네 가지 명확한 역할로 분리되자, 비로소 그 의미가 보이기 시작했다. 시작하는 규칙, 계속할지 결정하는 규칙, 매번 해야 할 일, 그리고 일이 끝난 뒤의 규칙.
“아…”
솔라는 작은 탄성을 내뱉었다.
“그러니까… for 문은 그냥 반복하라는 명령이 아니라, 반복을 어떻게 시작하고, 언제까지 계속하고, 한 번 끝날 때마다 무엇을 바꿀지 전부 알려주는 설명서 같은 거구나.”
“정확해. 반복에 필요한 모든 재료를 한곳에 모아둔 꾸러미지.”
솔라는 다시 모니터의 코드를 쳐다봤다. 이제는 더 이상 복잡한 암호문으로 보이지 않았다. for라는 글자 뒤의 괄호 안에서 시작점(int i = 0;)과 문지기의 질문(i < 5;), 그리고 다음 단계로 넘어가는 방법(i++)이 뚜렷하게 구분되었다. 그리고 그 세 가지 규칙의 통제를 받으며 반복될 실제 임무(print("안녕하세요!");)가 보였다.
복사 붙여넣기는 그저 똑같은 행동을 흩뿌려 놓는 것이었다. 하지만 for 문은 반복의 ‘제어’에 관한 것이었다. 시작, 조건, 변화. 이 세 가지 요소가 반복의 정체성을 규정하고 있었다.
“알겠어. 이제 각 부분이 무슨 역할을 하는지는 알겠어. 시작 담당, 조건 담당, 변화 담당, 그리고 실제 일 담당. 이렇게 네 부분인 거네.”
솔라는 고개를 끄덕이며 자신감을 되찾았다. 하지만 표를 자세히 들여다보던 솔라의 눈에 다시 궁금증이 떠올랐다. 루나가 일부러 다르게 붙인 번호가 계속 마음에 걸렸다.
“그런데 언니, 왜 번호가 ①, ②, ④, ③ 순서야? ①, ②, ③, ④가 아니고?”
솔라는 이제 각 구성 요소가 무엇인지는 알게 되었지만, 그것들이 어떤 순서로 춤을 추며 상호작용하는지는 또 다른 문제라는 것을 직감했다. 시작은 한 번뿐일 것 같고, 조건 검사는 여러 번 할 것 같은데… 그 흐름이 머릿속에서 명확하게 그려지지 않았다.
“이 네 가지 재료가 모여서 어떻게 하나의 동작을 만들어내는 거지? 컴퓨터는 대체 어떤 순서로 이 칸들을 읽어 나가는 거야?”
3장: for 문, 이렇게 움직인다! 실행 순서 따라가기
솔라의 시선은 루나가 그린 네 칸짜리 표에 고정되어 있었다. 각 칸의 역할은 이제 알겠다. 하지만 루나가 일부러 붙인 듯한 이상한 번호 순서, ①, ②, ④, ③은 여전히 풀리지 않는 수수께끼처럼 느껴졌다. 컴퓨터가 코드를 읽는 순서가 뒤죽박죽일 리는 없었다. 솔라의 궁금증 어린 시선을 본 루나는 말없이 펜을 다시 들었다.
루나는 기존 표 옆의 빈 공간에 새로운 표를 그리기 시작했다. 이번에는 칸이 더 많았다. ‘단계’, ‘실행되는 코드’, ‘i의 값’, ‘조건 검사 (i < 5)’, ‘결과’라는 제목들이 차례로 적혔다. 마치 기계의 작동 과정을 한순간도 놓치지 않고 기록하려는 정밀한 관찰 일지 같았다.
“네 질문에 대한 답은 여기에 있어. 컴퓨터의 시선으로 이 코드를 한 걸음씩 따라가 보자.”
루나는 표의 첫 번째 줄, ‘단계 ①’ 칸에 동그라미를 쳤다.
“자, 시작. 컴퓨터는 for 문을 만나면 가장 먼저 괄호 안의 첫 번째 부분, 초기식을 실행해. 딱 한 번만.”
루나는 ‘실행되는 코드’ 칸에 int i = 0;이라고 적고, ‘i의 값’ 칸에는 0을 썼다.
| 단계 | 실행되는 코드 | i의 값 | 조건 검사 (i < 5) | 결과 |
|---|---|---|---|---|
| ① | int i = 0; | 0 |
“이제 카운터 i가 0이 됐어. 그 다음은 어디로 갈까?”
루나가 솔라를 보며 물었다. 솔라는 지난 장에서 봤던 번호 순서를 떠올렸다. ① 다음은 ②였다.
“두 번째 칸, 조건 검사?”
“맞아. 컴퓨터는 조건식으로 가서 i가 5보다 작은지 확인해.”
루나는 표의 다음 줄에 내용을 채워 넣었다. ‘i의 값’은 여전히 0이었고, ‘조건 검사’ 칸에는 ‘0 < 5’라고 적혔다. 결과는 당연히 ‘참(True)’이었다.
| 단계 | 실행되는 코드 | i의 값 | 조건 검사 (i < 5) | 결과 |
|---|---|---|---|---|
| ① | int i = 0; | 0 | ||
| ② | i < 5 | 0 | 0 < 5 | 참 (True) |
“문지기가 문을 열어줬네. 그럼 이제 뭘 해야 할까?”
“일을 해야지! ‘안녕하세요!’ 출력.”
“정확해. 조건이 참이니까, 컴퓨터는 드디어 중괄호 안의 종속 문장을 실행해.”
루나가 표를 채우자, 실행 창에 ‘안녕하세요!’가 처음으로 찍히는 모습이 눈에 보이는 듯했다. 이제 i는 0이고, 인사는 한 번 했다. 솔라는 다음 단계를 생각했다. 이제 남은 건 변화식뿐이었다.
“그럼 이제 네 번째 칸, i++를 실행해서 i를 1로 만드는 거구나.”
루나는 고개를 끄덕이며 마지막 칸까지 채웠다.
| 단계 | 실행되는 코드 | i의 값 | 조건 검사 (i < 5) | 결과 & 출력 |
|---|---|---|---|---|
| ① | int i = 0; | 0 | ||
| ② | i < 5 | 0 | 0 < 5 | 참 (True) |
| ③ | print("안녕하세요!"); | 0 | ”안녕하세요!” (1번째) | |
| ④ | i++ | 1 |
“자, 이제 i는 1이 됐어. 첫 번째 반복이 끝난 셈이야. 그럼… 다음은?” 루나가 다시 물었다.
솔라는 잠시 망설였다. 초기식, 조건식, 종속 문장, 변화식을 모두 한 번씩 거쳤다. 이제 다시 처음으로 돌아가야 할까? 솔라의 머릿속에 의문이 스쳤다. ‘초기식도 매번 실행되나?’
“음… 다시 ①번, int i = 0;으로 돌아가는 건가?”
“만약 그렇다면 어떻게 될까?”
루나의 반문에 솔라는 상상해 보았다. 만약 i++로 i를 1로 만든 직후, 다시 int i = 0;이 실행된다면 i는 영원히 0이 될 것이다. 그럼 조건식 i < 5는 항상 참이 되고, 반복은 절대 끝나지 않는다. ‘안녕하세요!’가 화면을 영원히 뒤덮는 끔찍한 광경이 떠올랐다.
“아니네! 초기식은 정말로 맨 처음에 딱 한 번만 실행되는 거구나.”
솔라는 자신의 실수를 깨닫고 표를 다시 뚫어져라 쳐다봤다. 시작을 건너뛴다면, 갈 곳은 한 군데밖에 없었다.
“알았다! ④번 변화식이 끝나면, 다시 ②번 조건 검사로 돌아가는 거야!”
솔라의 외침에 루나는 미소를 지으며 펜으로 화살표를 그렸다. ④번 단계에서 ②번 단계로 돌아가는 커다란 순환의 고리였다.
① (한 번만!) → ② → ③ → ④ ⤵ ㅤㅤㅤㅤㅤㅤㅤ ⤴
“바로 그거야. for 문의 핵심은 바로 이 순환이지. ②번, ③번, ④번이 한 묶음으로 계속 도는 거야. 초기식은 이 궤도에 진입하기 위한 최초의 추진력일 뿐이고.”
이제 모든 것이 명확해졌다. 컴퓨터는 i가 1이 된 상태에서 다시 i < 5를 검사하고(참), ‘안녕하세요!’를 출력한 뒤(두 번째), i를 2로 만든다. 이 과정은 i가 4일 때까지 계속된다. i가 4일 때 ‘안녕하세요!’를 다섯 번째로 출력하고, i는 5가 된다.
“그럼 i가 5가 되면…?”
“다시 ②번으로 돌아가서 조건을 검사하지. ‘5 < 5’는…?”
“거짓(False)!”
“맞아. 문지기가 처음으로 ‘안 돼!’라고 외치는 순간이야. 그럼 컴퓨터는 ③번과 ④번을 쳐다보지도 않고, 그대로 for 문 전체를 빠져나가는 거지.”
솔라는 비로소 모든 조각이 맞춰지는 것을 느꼈다. 왜 루나가 표에 ①, ②, ④, ③ 순으로 번호를 매겼는지도 이제는 이해할 수 있었다. 실제로 코드를 실행하는 순서는 ① → ② → ③ → ④ 순이지만, 개념적으로는 문지기(②)의 허락을 받고 할 일(③)을 한 뒤, 다음을 위해 상태를 바꾸는(④) 흐름이 반복되기 때문이었다. ④번은 ③번의 결과에 따라오는 후속 조치였던 것이다.
“이제 알겠어. for 문은 그냥 괄호 안의 규칙들을 순서대로 실행하는 게 아니었어. 시작 규칙, 그리고 조건-실행-변화라는 하나의 묶음이 계속 순환하는 거였구나.”
더 이상 for 문은 복잡한 암호가 아니었다. 명확한 흐름을 가진, 예측 가능한 기계 장치처럼 보였다. 어떤 부분을 보더라도 바로 다음 동작을 정확히 예측할 수 있을 것 같았다.
루나는 만족스럽게 고개를 끄덕이고는, 새로운 과제를 제시했다.
“좋아. 그럼 이 기계장치를 직접 조종해볼까? ‘안녕하세요’ 다섯 번 대신, 화면에 숫자 1, 2, 3을 차례대로 출력하려면 이 코드를 어떻게 바꿔야 할까?”
스스로 설계해보라는 말에 솔라는 잠시 생각에 잠겼다. 하지만 이제는 막막하지 않았다. 네 가지 핵심 구성 요소와 그들의 실행 순서를 알고 있었다.
“음… 일단 숫자를 세는 i를 출력해야 하니까, print("안녕하세요!")는 print(i)로 바뀌어야 해. 그리고 1부터 시작해야 하니까, 초기식은 int i = 1;이 맞겠네. 3까지 출력해야 하니까… 조건식은 i < 4 또는 i <= 3이면 되겠다. i가 3일 때도 실행되어야 하니까. 그리고 숫자가 하나씩 커지니 변화식은 그대로 i++를 쓰면 되고.”
솔라는 자신의 논리를 따라 직접 코드를 수정했다.
for (int i = 1; i <= 3; i++) {
print(i);
}
엔터 키를 누르자, 결과 창에 거짓말처럼 1, 2, 3이 차례로 찍혔다. 자신이 직접 설계한 규칙에 따라 컴퓨터가 정확하게 움직이는 것을 보자 짜릿한 희열이 느껴졌다. for 문은 반복을 제어하는 규칙들의 묶음이라는 사실이 머리가 아닌 손으로 이해되는 순간이었다.