PowerShell
Step 8. 조건문과 반복문 — 컴퓨터에게 판단과 노가다를 시키기
Level 0 — 컴퓨터 조작과 구조의 이해 | 난이도 ★★☆☆☆ | 예상 소요 시간 3시간
전제: Step 7 완료. Windows PowerShell 5.1 또는 Windows의 PowerShell 7에서 진행합니다. 인터넷 연결은 필요 없습니다.
- 준비물: 윈도우 PC, 파워쉘.
- 안전: 작은 가상 배열과 새 전용 폴더의 로그만 다룹니다. 기존 파일이나 실제 계정에 반복 작업을 적용하지 않습니다.
- 주의: 비교 기호가 특이합니다. 파워쉘은
>대신-gt,=대신-eq를 씁니다. 어색해도 규칙은 단순하니, 한 번 틀리고 나면 바로 익숙해집니다.
Step 4에서는 로그를 검색했고 Step 7에서는 결과를 변수에 담았습니다. 이제 “기준 이상이면 알리기”와 “각 줄을 순서대로 검사하기”를 코드로 만듭니다. 실행 시간은 데이터 크기와 작업에 따라 달라집니다. 오늘은 작은 가상 데이터로 결과를 직접 계산해 보고 코드 출력과 비교합니다.
1. 학습 목표
이 챕터를 끝내면 다음을 할 수 있습니다:
if/else로 "만약 ~라면" 판단을 코드로 옮긴다foreach로 목록 전체를 자동으로 훑고,for로 정해진 횟수를 반복한다- 조건 + 반복을 합쳐 "조건에 맞는 것만 골라내기(필터링)"를 만든다
- 누적 패턴(합계·건수 세기)으로 1~100의 짝수 합계 스크립트를 완성한다
2. 배경 지식 — 오늘의 도구와 개념
오늘의 도구 한눈에 보기
| 구분 | 내용 |
|---|---|
| 언어·환경 | Windows PowerShell 5.1 / PowerShell 7 — 조건과 반복 |
| 오늘의 문법 | if (조건) { } else { }, foreach ($x in $목록) { }, for ($i=1; $i -le N; $i++) { } |
| 비교 연산자 | -eq -ne -gt -lt -ge -le (그리고 나머지 %) |
| 필요한 개념 | 제어 흐름, 조건식(참/거짓), 반복, 누적 패턴 |
변수가 알파벳이라면, 조건문과 반복문은 문법입니다. 두 개념 모두 일상에서 이미 쓰고 있는 것들입니다 — "비가 오면 우산을 챙긴다"(조건), "엘리베이터가 올 때까지 기다린다"(반복). 오늘은 이걸 코드로 옮기는 연습일 뿐입니다.
2-1. 제어 흐름 — 물줄기에 댐과 수로 놓기
프로그램은 기본적으로 위에서 아래로 흐릅니다. 이 흐름을 제어 흐름(control flow)이라 합니다. 오늘 배우는 두 도구는 이 흐름에 다는 장치입니다:
- 조건문(if) — "이 조건이 참이면 A로, 아니면 B로" 흐름을 갈라놓는 수문
- 반복문(foreach, for) — 같은 구간을 여러 바퀴 도는 순환 수로
Python·C·JavaScript 등에서도 조건과 반복을 만납니다. 익힌 개념은 도움이 되지만 참·거짓 변환, 범위와 문법 규칙은 언어마다 다르므로 같은 동작이라고 가정하지 않습니다.
2-2. 조건식 — 참/거짓으로 답하는 질문
if는 조건의 결과를 참·거짓으로 해석합니다. 아래의 단일 숫자 비교는 Boolean 값을 반환합니다. 다만 배열을 왼쪽에 둔 비교는 일치하는 원소들을 반환하므로 모든 비교식의 출력이 항상 True/False인 것은 아닙니다.
$a -gt 5 — "a가 5보다 큰가?" a가 7이면 참, 3이면 거짓. 파워쉘의 비교 연산자 표입니다:
| 연산자 | 뜻 | 예시 | 결과 |
|---|---|---|---|
-eq |
같다 (equal) | $a -eq 5 |
a가 5면 참 |
-ne |
다르다 (not equal) | $a -ne 5 |
a가 5가 아니면 참 |
-gt |
크다 (greater than) | $a -gt 5 |
a가 5보다 크면 참 (예: 5.5) |
-lt |
작다 (less than) | $a -lt 5 |
a가 5보다 작으면 참 (예: 4.5) |
-ge |
크거나 같다 | $a -ge 5 |
a가 5 이상이면 참 |
-le |
작거나 같다 | $a -le 5 |
a가 5 이하면 참 |
주의: =는 대입, 같은지 비교는 -eq입니다. >는 출력 리디렉션에 쓰이므로 “크다” 비교에는 -gt를 사용합니다. 경계와 소수, 배열 결과를 직접 비교합니다:
5 -gt 5
5 -ge 5
5.5 -gt 5
@(3, 7, 2, 8) -ge 5
False
True
True
7
8
5.5도 5보다 큽니다. 마지막 줄의 7·8은 배열에서 고른 원소입니다. 문자열 비교의 -eq는 기본적으로 대소문자를 구분하지 않으며, 구분하려면 -ceq를 씁니다.
2-3. 반복의 두 얼굴
반복에는 상황에 따라 두 가지가 쓰입니다:
- foreach — "목록의 항목마다 하나씩" (멤버 명단을 앞에서부터 쭉 호명)
- for — "정해진 횟수만큼" (10바퀴 뛰어라)
목록 순회에는 foreach, 시작값·계속 조건·증감을 직접 제어할 때는 for가 읽기 쉽습니다. “for는 항상 정해진 횟수만 돈다”는 보장은 없습니다. 초기 조건이 거짓이면 0번, 종료 조건이 성립하지 않으면 계속 반복할 수 있습니다. 오늘 예제는 범위가 유한합니다.
3. 따라 하기
3-1. 첫 조건문 — if
$a = 7
if ($a -gt 5) { "5보다 큼" } else { "5 이하" }
5보다 큼
출력 읽는 법: $a가 7이니 "5보다 큰가?"는 참 → { "5보다 큼" } 쪽이 실행됐습니다.
구조 분해: if (조건식) { 참일 때 할 일 } else { 거짓일 때 할 일 }. 중괄호 { }가 "할 일 묶음"입니다.
왜 하는가: 이제 $a를 3으로 바꾸고 다시 실행해 보세요:
$a = 3
if ($a -gt 5) { "5보다 큼" } else { "5 이하" }
이번엔 5 이하가 나옵니다. $a가 정확히 5일 때도 같은 else 분기로 갑니다. 같은 코드인데 값에 따라 다른 동작 — 이것이 조건문의 힘입니다. 코드가 스스로 상황에 반응하기 시작한 겁니다.
3-2. else 없는 if, 그리고 실전형 조건
거짓일 때 할 일이 없으면 else는 생략합니다:
$failCount = 7
if ($failCount -ge 5) { "경고: 로그인 실패가 너무 많습니다!" }
경고: 로그인 실패가 너무 많습니다!
왜 하는가: 예시 기준 5 이상이면 경고합니다. $failCount를 2로 바꾸면 이 if는 출력하지 않습니다. 기준 5는 가상 데이터용 선택이며 실제 침해 판정 기준은 아닙니다. 실제 규칙에는 관찰 시간대, 사용자·장치 범위, 데이터 누락 여부 등도 필요합니다.
예측해 보기:
if ($failCount -gt 5) { ... }로-ge대신-gt를 쓰면, 실패가 정확히 5건일 때 경고가 뜰까요 안 뜰까요? (정답: 안 뜹니다.-gt는 "초과"라서 5는 포함하지 않습니다. 경계값 — 딱 기준 숫자 — 에서 동작이 어떻게 달라지는지가 프로그래밍 실수의 단골 자리입니다. 직접 5를 넣어 확인해 보세요.)
3-3. foreach — 목록 자동 순회
Step 7의 배열을 꺼내 옵시다:
$members = @("Kim", "Lee", "Park")
foreach ($m in $members) { "안녕, $m!" }
안녕, Kim!
안녕, Lee!
안녕, Park!
출력 읽는 법: 목록의 항목마다 한 번씩, 총 세 번 실행됐습니다.
구조 분해: foreach ($m in $members) — "members 목록에서 하나씩 꺼내 m이라 부르면서" { } 안을 실행합니다. $m은 매 바퀴 바뀝니다: 첫 바퀴 Kim, 둘째 바퀴 Lee, 셋째 바퀴 Park.
왜 하는가: 이 패턴이 실전에서는 이렇게 변합니다 — "파일 목록에서 하나씩 꺼내 검사", "사용자 목록에서 하나씩 꺼내 권한 확인". 100개든 100만 개든 코드는 똑같이 한 줄입니다.
3-4. for — 정해진 횟수 반복
for ($i = 1; $i -le 5; $i++) { "숫자 $i" }
숫자 1
숫자 2
숫자 3
숫자 4
숫자 5
구조 분해 — 괄호 안이 세 조각입니다:
$i = 1— 시작값 (i를 1로 놓고 시작)$i -le 5— 계속할 조건 (i가 5 이하인 동안만 반복)$i++— 매 바퀴 끝에 할 일 (i에 1을 더해라 —++는 "1 증가"라는 뜻의 기호)
왜 하는가: 1~100의 합계나 번호가 붙은 시험 데이터처럼 시작·끝 범위가 있는 작업에 씁니다. 이 수업에서는 화면 출력과 생성한 로컬 실습 파일만 다룹니다.
3-5. 조건 + 반복 합체 — 필터링
이제 둘을 합쳐 봅시다. "목록에서 조건에 맞는 것만 골라내기":
$scores = @(85, 42, 91, 55, 78)
foreach ($s in $scores) {
if ($s -ge 60) { "$s 점: 합격" }
}
85 점: 합격
91 점: 합격
78 점: 합격
출력 읽는 법: 42와 55는 조건(60 이상)에 안 맞아 걸러졌습니다. 반복문이 전부 돌되, 조건문이 출력을 선별한 겁니다.
왜 하는가: "전부 훑되 조건 맞는 것만 처리" — 이것이 로그 분석의 정확한 작동 원리입니다. 수만 줄을 foreach로 돌리고, "ERROR 포함" 같은 조건으로 낚는 것. 오늘 이 5줄이 그 원리의 축소판입니다.
3-6. 대망의 종합 — 짝수의 합 구하기
오늘의 완료 기준 과제를 같이 해 봅시다: 1부터 100까지 중 짝수만 더하기.
짝수 판별법부터: 어떤 수를 2로 나눈 나머지가 0이면 짝수입니다. 나머지 연산자는 %입니다. $n % 2가 0이면 짝수.
$sum = 0
for ($i = 1; $i -le 100; $i++) {
if ($i % 2 -eq 0) { $sum = $sum + $i }
}
$sum
2550
한 줄씩 해설:
$sum = 0— 합계를 담을 상자를 0으로 준비 (비어 있는 주머니)- 1부터 100까지 반복하되
$i % 2 -eq 0— 나누어 떨어지면(짝수면)$sum = $sum + $i— 주머니에 그 수를 더해라. 오른쪽이 먼저 계산되고 결과가 다시 $sum에 들어갑니다- 최종 출력: 2550 (2+4+6+…+100, 실측 확인)
왜 하는가: "누적"(반복하면서 값을 쌓아 올리기)은 데이터 처리의 기본 패턴입니다. 총 에러 수, 총 전송량, 평균 계산 — 전부 이 모양입니다.
3-7. 실전 연결 — Step 4의 로그와 합치기
오늘의 기술이 실제 로그 파일에서 어떻게 쓰이는지, 맛보기로 연결해 봅시다. 먼저 실험용 로그를 만듭니다 (Step 4의 생성기와 같은 요령):
$looplab = Join-Path $HOME ('infosec-step08-' + [guid]::NewGuid().ToString('N'))
New-Item -ItemType Directory -Path $looplab -ErrorAction Stop
Set-Location -LiteralPath $looplab
pwd
1..20 | ForEach-Object {
if ($_ % 3 -eq 0) { "ERROR 번호 $_" }
else { "OK 번호 $_" }
} | Set-Content scanlog.txt -Encoding UTF8
새 폴더 경로를 확인하세요. 생성기를 다시 실행해도 같은 scanlog.txt를 20줄로 다시 만듭니다. 이어서 같은 세션·폴더에서 UTF8로 읽어 배열에 담고 조건으로 셉니다:
$lines = @(Get-Content scanlog.txt -Encoding UTF8)
$errCount = 0
foreach ($line in $lines) {
if ($line.Contains("ERROR")) { $errCount = $errCount + 1 }
}
"전체 $($lines.Count)줄 중 에러 $errCount 건"
전체 20줄 중 에러 6 건
한 줄씩 해설:
Get-Content— 파일의 결과를@(...)로 감싸 배열로 받습니다. 20줄이면 20개 원소이며 0·1줄 파일도 배열로 다룰 수 있습니다$errCount = 0— 누적 주머니 준비 (3-6절의 패턴)foreach— 20줄을 하나씩 돌면서$line.Contains("ERROR")— "이 줄에 ERROR라는 글자가 포함되어 있나?"라는 조건식. 대소문자까지 일치하는 부분 문자열이 있으면 참- 최종 보고 — 큰따옴표 안의
$($lines.Count)표기 기억하시죠?
왜 하는가: 파일을 읽고, 줄을 순회하고, 조건에 맞는 줄을 세어 요약하는 작은 분석 예제입니다. Contains(“ERROR”)는 대소문자를 구분하는 부분 문자열 검사입니다. 소문자 error는 다르고 NO_ERROR 같은 문자열도 ERROR를 포함합니다. 여기서 센 것은 일치하는 줄 수이며, 실제 오류 사건 수나 침해 건수로 바로 해석하지 않습니다. 대규모 로그에서는 형식 파싱·중복 처리·메모리와 스트리밍도 고려합니다.
4. 미션과 연습문제
미션 — 로그인 감시 스크립트 (축소판)
다음 값은 한 사용자의 시간대별 실패 횟수를 가정한 가상 배열입니다. 실제 계정이나 서버에 로그인 요청을 보내지 않습니다. 배열에 지정된 건수를 합산하는 조건·반복 연습입니다.
$failLog = @(0, 1, 0, 3, 9, 12, 2, 0)
과제:
- foreach로 이 목록을 돌면서, 실패가 5건 이상인 시간대만
"위험: N건 실패"라고 출력하세요 - 반복이 끝난 뒤, 전체 실패 합계를
"총 실패: M건"으로 출력하세요 (누적 패턴 활용)
연습문제
문제 1. $a = 5와 $a -eq 5의 차이를 설명하세요.
문제 2. foreach와 for 중 어떤 것을 써야 할지 구분하는 기준은?
문제 3. @(3, 7, 2, 8)에서 5 이상만 출력하는 코드를 써 보세요.
문제 4. 누적 패턴 스크립트를 두 번 연속 실행했더니 합계가 두 배가 됐습니다. 원인과 해결은?
해설과 완료 기준 · 펼치기/접기
5. 모범 답안과 완료 기준
미션 모범 답안
한글과 변수를 붙일 때: $f건은 변수 f 뒤에 “건”을 붙이는 표기가 아니라 f건이라는 변수 이름으로 읽힙니다. $($f)건 또는 ${f}건처럼 경계를 지정하세요. 원래 한글 예제에서 숫자가 빠지던 원인을 수정했습니다.
$failLog = @(0, 1, 0, 3, 9, 12, 2, 0)
$sum = 0
foreach ($f in $failLog) {
if ($f -ge 5) { "위험: $($f)건 실패" }
$sum = $sum + $f
}
"총 실패: $($sum)건"
기대 출력 (실측 확인):
위험: 9건 실패
위험: 12건 실패
총 실패: 27건
검증하는 법: "위험"이 정확히 두 줄이고 총합이 27이면 완벽합니다. 위험이 세 줄 이상이면 조건을 다시 보세요. 총합이 다르면 $sum 초기화(= 0)를 빼먹었을 가능성이 큽니다.
심화: 조건을 -gt 5로 바꾸면 어떤 줄이 사라질까요? 이유와 함께 예측한 뒤 확인해 보세요. (정답: 없습니다. 9와 12 모두 5 초과라 결과가 같습니다. 대신 기준을 9로 올린 -ge 9 vs -gt 9를 비교해 보면 경계값 차이가 확실히 보입니다.)
연습문제 해답
문제 1 해답. $a = 5는 대입 — 상자에 5를 넣는 동작입니다. $a -eq 5는 비교 — "a가 5와 같은가?"라는 질문으로 답은 참/거짓입니다. 조건 자리에 =를 쓰는 것이 초보 최대의 버그입니다.
문제 2 해답. 다룰 목록이 이미 있으면 foreach ("목록의 항목마다 하나씩"), 횟수나 숫자 범위가 정해져 있으면 for ("1부터 100까지")를 씁니다.
문제 3 해답.
$nums = @(3, 7, 2, 8)
foreach ($n in $nums) {
if ($n -ge 5) { $n }
}
출력은 7과 8입니다. "전부 훑되 조건 맞는 것만 처리" — 필터링 패턴입니다.
문제 4 해답. 초기화 줄을 실행하지 않고 누적 부분만 다시 실행했거나 입력 파일이 중복되었을 수 있습니다. 같은 세션의 이전 값이 남는지 확인하고, $sum = 0을 포함한 전체 블록과 실험용 로그 생성 블록을 각각 처음부터 실행하세요. 반복문 안에서 합계를 계속 0으로 만들면 누적되지 않습니다.
완료 기준 체크리스트
- [ ]
-eq,-gt,-lt등 비교 연산자를 구분해 쓸 수 있다 - [ ]
=와-eq의 차이를 설명할 수 있다 - [ ] if/else로 조건에 따른 분기를 작성할 수 있다
- [ ] foreach로 배열의 모든 항목을 순회할 수 있다
- [ ] for로 횟수 반복을 작성할 수 있다
- [ ] 누적 패턴으로 합계를 구할 수 있다
- [ ] 미션: 로그인 감시 스크립트(축소판)를 완성했다
6. 흔한 실수와 해결
벽 1. =와 -eq를 헷갈린다
동등 비교는 -eq입니다. 조건에 필요한 다른 비교는 -gt·-ge 등으로 표현할 수 있습니다. if ($a = 5)는 값을 대입한 뒤 그 값으로 판단하므로 비교문과 다릅니다. 5는 참으로, 0은 거짓으로 해석됩니다. 문자열 “False”는 비어 있지 않아 참이며 Boolean $false와 구별합니다.
벽 2. 반복문이 한 번도 안 돌거나 끝나지 않는다
증상: for문을 쳤는데 출력이 없거나, 멈추지 않음.
원인: 조건을 거꾸로 쓴 경우가 많습니다. for ($i=1; $i -ge 100; $i++)처럼 시작하자마자 조건이 거짓이면 한 번도 안 돕니다. 반대로 조건이 영원히 참이면 무한 반복.
해결: 멈추지 않으면 Ctrl + C로 중단하세요. 그리고 "시작값이 조건을 만족하는가", "매 바퀴 조건이 거짓 쪽으로 가는가" 두 가지만 점검하면 됩니다.
벽 3. 중괄호 { } 짝이 안 맞는다
>>는 입력이 아직 완성되지 않았다는 표시입니다. 중괄호뿐 아니라 따옴표·괄호·행 끝 파이프도 원인일 수 있습니다. 열린 구조를 확인해 완성하거나 Ctrl+C로 현재 입력을 취소합니다. 실행 중 루프를 중단해도 이미 수행한 파일 작업은 되돌아가지 않습니다.
벽 4. 합계가 이상하게 크거나 0이다
합계 변수는 반복 시작 전에 한 번 0으로 초기화합니다. 반복문 안에서 매번 초기화하면 마지막 값만 남을 수 있고, 이전 실행의 값을 유지한 채 반복 부분만 실행하면 두 배가 될 수 있습니다. 소스 파일이 중복 생성되었는지도 확인하세요. 수정된 전체 블록을 처음부터 실행하면 합계 2550, 로그 20줄 중 ERROR 6줄을 다시 얻어야 합니다.
벽 5. -gt와 -ge의 경계에서 헷갈린다
증상: "5 이상"인데 5에서 동작을 안 함.
원인: -gt(초과)를 써서 5가 제외됐습니다. "이상"은 -ge, "초과"는 -gt.
해결: 기준 숫자 자체를 넣어 테스트해 보세요(경계값 테스트). "정확히 5일 때 어떻게 되어야 하는가"를 먼저 정하고 코드를 고르면 실수가 줄어듭니다.
7. 정리
오늘의 두 기둥
| 도구 | 용도 | 형태 |
|---|---|---|
| if / else | 조건에 따라 갈림길 | if (조건) { A } else { B } |
| foreach | 목록 전체 순회 | foreach ($x in $list) { ... } |
| for | 횟수 반복 | for ($i=1; $i -le N; $i++) { ... } |
비교 연산자 암기표
-eq(같다) -ne(다르다) -gt(크다) -lt(작다) -ge(이상) -le(이하) — 그리고 =는 대입, %는 나머지.
오늘의 패턴 카드
| 패턴 | 코드 골격 | 실전 용도 |
|---|---|---|
| 임계값 경고 | if ($n -ge 기준) { 경고 } |
실패 로그인 알림 |
| 필터링 | foreach 안에 if | 로그에서 이상 징후만 추출 |
| 누적 | $sum = 0 후 반복하며 더하기 |
총 건수·총량 집계 |
명령어보다 중요한 감각
침해 탐지 규칙, 로그 분석, 알림 시스템 — 전부 오늘의 "조건 + 반복" 위에 서 있습니다. "5회 이상 실패 시 경고"를 코드로 쓸 수 있다는 것은, 여러분이 더 이상 로그를 눈으로 읽는 사람이 아니라 로그를 검사하는 프로그램을 만드는 사람이 됐다는 뜻입니다.
같은 도구가 공격과 수비 양쪽에서 쓰인다는 것도 기억하세요. 공격자는 for로 비밀번호 후보를 하나씩 대입하고(무차별 대입 — Level 2에서 랩 환경에서 직접 실습합니다), 수비자는 foreach로 접속 로그를 훑어 이상 징후를 찾습니다. 방화벽 규칙과 접근 통제도 결국 거대한 if문의 집합입니다.
전부 체크되면 Step 8 완료입니다.
실습 검증 기록
2026-09-10, Windows PowerShell 5.1.26100.9444와 PowerShell 7.6.5에서 한글·영문 코드를 각각 순서대로 실행했습니다. 경계값 5, 소수 비교, 1~100 짝수 합 2550, 로그 재실행 후 20줄·ERROR 6줄, 실패 합계 27과 경고 두 줄을 확인했습니다. 한글 접미사가 변수 이름에 포함되어 숫자가 빠지던 원본 오류도 재현했습니다. 모든 파일 작업은 새 실습 폴더에서 수행했습니다.
Microsoft: Comparison operators · If · For · String.Contains · Quoting rules
한 단계 더 가까이
수업을 마쳤나요?
본문의 완료 기준을 확인한 뒤 직접 표시하세요.