예상 모의 1회 실전 E29

이전 자료의 예상 모의를 실전 꼴로 옮겼다. 기출과 같은 분포(프로그래밍 9 · SQL·DB 4 · 네트워크 2 · 보안 2 · SW공학 3)로 짰다

왜 이 장을 하나
예상 모의는 기출에 아직 안 나온 꼴을 기출과 같은 분포로 섞은 세트다. 자릿수 합 재귀, sorted key=d.get, 2차원 배열을 1차원 포인터로, GRANT WITH GRANT OPTION, 부분 함수 종속, /26 서브넷, 교착상태 4조건. 기출 10세트를 다 돌린 뒤 새 문제에서도 절차가 나오는지 보는 자리다.
다루는 것예상 모의 1회 20문항 · 복원본
시계150분 · 실제 시험과 같다
끝나면문항마다 가는 장이 표로 나온다

01푸는 법

왜 이걸 하나 — 세트는 시험장과 같은 조건에서만 숫자가 된다. 조건은 E01 과 같다.

규칙은 E01 진단의 01절 그대로다. 시작을 누르면 150분이 돌고, 1회독 100분 · 2회독 40분 · 검토 10분으로 나눈다. 한 문항에 5분이 넘으면 답 칸을 비우고 넘어가며, 정답은 20문항을 다 적은 뒤에 연다.

이 회차의 배점은 프로그래밍 9문항(45점) · SQL·DB 4문항(20점) · SW공학 3문항(15점) · 네트워크 2문항(10점) · 보안 2문항(10점)이다. 코드 문항 수만큼 5분씩을 1회독에서 먼저 떼어 두고, 남는 시간을 용어에 쓴다. 용어는 아는 것을 바로 적고 모르는 것은 비운다 — 용어에서 고민하는 시간이 코드 한 문항의 값이다.

자가 판정은 답안지에 그대로 적었을 때 채점자가 맞다고 할 것인가 하나다. 철자 하나, 줄바꿈 하나, 기호 대신 이름이면 틀렸다를 누른다. 후하게 준 5점은 시험장에서 사라진다.

02실전 세트

왜 이걸 하나 — 20문항을 한 번에 150분 안에 푸는 것 자체가 연습이다. 분야 장의 문제와 같은 문항이어도 조건이 다르다.

시작을 누르면 시계가 돌고 화면을 닫아도 이어진다. 다 적은 뒤 문항마다 정답 보기 → 맞았다 · 틀렸다를 누르면 위 막대에 점수와 분야별 결과가 모인다. 이 세트에서 채점한 기록은 분야 장의 같은 문항에도 그대로 보인다.

예상 1회 · 1번Java

다음 코드의 출력을 쓰시오.

class Animal { String name = "Animal"; void cry() { System.out.print("A"); } void sound() { cry(); System.out.print(name); } } class Dog extends Animal { String name = "Dog"; void cry() { System.out.print("D"); } } public class Main { public static void main(String[] args) { Animal a = new Dog(); a.sound(); System.out.print(a.name); } }
정답DAnimalAnimal

— DAnimalAnimal. 메서드는 자식 것이 불리지만(D), 필드는 참조 타입인 Animal 것이 읽힌다(Animal · Animal).

원리 — 자바에서 메서드 호출은 "실제 객체"를 보고 결정된다(동적 바인딩). a 는 Animal 타입 변수지만 안에 든 것은 Dog 이므로 cry() 는 Dog 의 것이 불려 D 가 찍힌다. 그런데 필드는 다르다 — 필드는 오버라이딩되지 않고 "가려질(hiding)" 뿐이며, 어느 필드를 읽을지는 컴파일 시점의 타입으로 정해진다. sound() 는 Animal 클래스 안에 적힌 코드라 거기서 name 은 Animal 의 name 이고, a.name 은 a 의 타입이 Animal 이라 역시 Animal 의 name 이다. Dog 의 name = "Dog" 는 Dog 타입 변수로 봐야만 보인다.

따라가기

  1. a.sound() → Animal 의 sound 실행 → 안의 cry() 는 Dog 것 → D
  2. sound 안의 name → Animal 코드 안 → Animal
  3. a.name → a 의 타입 Animal → Animal
  4. 합쳐 DAnimalAnimal

헷갈리는 자리 — 필드도 오버라이딩된다고 생각해 DDogDog 로 적는 것. "메서드는 객체를 보고, 필드는 타입을 본다" 한 줄로 잡는다. 필드에 자식 값을 쓰게 하려면 getName() 같은 메서드로 감싸야 한다 — 그것이 캡슐화가 권장되는 이유 중 하나다.

이전 자료의 예상 문제다. 코드를 손으로 따라가고 JDK 17 실행 결과와 대조했다.

예상 1회 · 2번C

다음 코드의 출력을 쓰시오.

char *s = "INFORMATION"; printf("%s\n", s + 6); printf("%c\n", *(s + 2)); printf("%c\n", *s + 2);
정답ATION / F / K

— ATION / F / K (세 줄). 포인터에 더하는 것과 글자에 더하는 것이 다르다는 것을 한 문제에 넣었다.

원리 — s 는 문자열 "INFORMATION" 의 첫 글자 주소를 담은 포인터다. s + 6 은 "여섯 글자 뒤의 주소"이고 %s 는 그 주소부터 널 문자까지 찍으니 7번째 글자부터 끝까지 나온다. *(s + 2) 는 "두 글자 뒤 주소의 내용" = 세 번째 글자 하나다. *s + 2 는 우선순위가 다르다 — * 가 + 보다 먼저라 "첫 글자의 내용"에 2 를 더한 것이고, 문자는 정수(아스키 코드)라 'I' 의 코드 73 + 2 = 75 = 'K' 가 된다. 괄호 위치 하나가 "주소를 옮기나, 글자를 바꾸나"를 가른다.

따라가기

  1. I(0) N(1) F(2) O(3) R(4) M(5) A(6) T I O N — 번호를 매긴다
  2. s + 6 → 6번부터 끝까지 → ATION
  3. *(s + 2) → 2번 글자 → F
  4. *s + 2 → 'I'(73) + 2 = 75 → K

헷갈리는 자리 — *s + 2 를 *(s + 2) 로 읽어 F 라고 적는 것. 괄호가 없으면 * 가 먼저 붙는다. 그리고 %c 로 찍으니 숫자 75 가 아니라 글자 K 다 — %d 였다면 75 가 찍힌다. 번호는 0 부터 세는 것도 늘 확인한다.

이전 자료의 예상 문제다. 코드를 손으로 따라가고 docker gcc(alpine 13.2) 실행 결과와 대조했다.

예상 1회 · 3번C

다음 코드의 출력을 쓰시오.

int f(int n) { if (n == 0) return 0; return n % 10 + f(n / 10); } int main() { printf("%d", f(1234)); }
정답10

— 10. 1234 의 각 자리 4 + 3 + 2 + 1 을 재귀로 더한 것이다.

원리 — n % 10 은 마지막 자리, n / 10 은 마지막 자리를 뗀 나머지 수다(정수 나눗셈이라 소수점이 버려진다). f(n) 은 "마지막 자리 + 나머지 수의 자릿수 합"으로 자기 자신을 부르고, n 이 0 이 되면 더할 것이 없으니 0 을 돌려준다. 재귀는 "지금 한 자리만 처리하고 나머지는 같은 함수에 맡긴다"는 발상이다. 호출은 f(1234) → f(123) → f(12) → f(1) → f(0) 순으로 쌓였다가 f(0) = 0 부터 거꾸로 값이 돌아오며 더해진다.

따라가기

  1. f(1234) = 4 + f(123)
  2. f(123) = 3 + f(12), f(12) = 2 + f(1), f(1) = 1 + f(0), f(0) = 0
  3. 되돌아오며 1 + 0 = 1 → 2 + 1 = 3 → 3 + 3 = 6 → 4 + 6 = 10

헷갈리는 자리 — 종료 조건을 n == 1 로 착각해 마지막 1 을 빠뜨리는 것. 이 코드는 n == 0 에서 멈추므로 1 의 자리도 더해진다. 그리고 순서 — 자릿수 합은 순서가 상관없지만, 자리를 "출력"하는 재귀라면 호출 순서(4 3 2 1)인지 반환 순서(1 2 3 4)인지가 답을 가른다.

이전 자료의 예상 문제다. 코드를 손으로 따라가고 docker gcc(alpine 13.2) 실행 결과와 대조했다.

예상 1회 · 4번Python

다음 코드의 출력을 쓰시오.

d = {'a': 3, 'b': 1, 'c': 2} s = sorted(d, key=d.get) print(s[0], d[s[-1]])
정답b 3

— b 3. 값 순으로 키를 정렬하면 b · c · a 이고, 첫 키는 b, 마지막 키 a 의 값은 3 이다.

원리 — sorted(d) 는 딕셔너리의 "키"를 정렬한 리스트를 돌려준다. key=d.get 을 주면 각 키를 d.get(키) 곧 그 키의 값으로 바꿔 비교한다 — 키가 아니라 값 순으로 키가 줄 서는 것이다. 값이 1 · 2 · 3 인 b · c · a 순서다. s[0] 은 'b', s[-1] 은 마지막 'a' 이고 d['a'] = 3 이다. "값으로 정렬한 키 목록"은 최댓값 키 찾기(max(d, key=d.get))와 같은 발상이라 함께 나온다.

따라가기

  1. d.get 기준: b(1) → c(2) → a(3) → s = ['b', 'c', 'a']
  2. s[0] → b
  3. s[-1] = 'a' → d['a'] → 3

헷갈리는 자리 — sorted(d) 가 값을 정렬한다고 보는 것. 딕셔너리를 순회하면 키가 나오므로 정렬 대상도 키다. key=d.get 이 없으면 알파벳 순 a · b · c 라 답이 "a 2" 로 바뀐다. sorted(d.values()) 였다면 [1, 2, 3] 이다 — 무엇을 정렬하는지 먼저 본다.

이전 자료의 예상 문제다. 코드를 손으로 따라가고 Python 3.12 실행 결과와 대조했다.

예상 1회 · 5번Java

다음 코드의 출력을 쓰시오.

int[] arr = {1, 2, 3}; try { System.out.print(arr[0]); System.out.print(arr[3]); System.out.print(arr[1]); } catch (ArrayIndexOutOfBoundsException e) { System.out.print("E"); } finally { System.out.print("F"); } System.out.print("G");
정답1EFG

— 1EFG. arr[3] 에서 예외가 나 그 뒤 줄은 건너뛰고 catch → finally → 다음 문장 순으로 간다.

원리 — try 블록은 예외가 나는 순간 거기서 멈추고 나머지 줄은 실행하지 않는다. arr 은 3칸(0 · 1 · 2)이라 arr[3] 은 범위 밖이고 ArrayIndexOutOfBoundsException 이 던져진다. 그래서 arr[0] 의 1 은 찍혔지만 arr[1] 의 2 는 영영 찍히지 않는다. 예외는 타입이 맞는 catch 로 잡혀 E 를 찍고, finally 는 예외가 났든 안 났든 반드시 실행돼 F 를 찍는다. 예외가 처리됐으니 프로그램은 계속 진행해 G 를 찍는다.

따라가기

  1. arr[0] → 1
  2. arr[3] → 예외 발생 → arr[1] 건너뜀
  3. catch → E → finally → F
  4. try 밖 → G. 출력 1EFG

헷갈리는 자리 — 예외 뒤의 arr[1] 도 찍혀 12EFG 로 적는 것. 예외가 난 줄부터 try 의 나머지는 실행되지 않는다. 그리고 catch 가 없거나 타입이 안 맞았다면 finally 는 F 를 찍고 예외가 밖으로 나가 G 는 안 찍힌다 — "잡혔는가"가 뒤 문장의 운명을 정한다.

이전 자료의 예상 문제다. 코드를 손으로 따라가고 JDK 17 실행 결과와 대조했다.

예상 1회 · 6번C

다음 코드의 출력을 쓰시오.

int a = 12, b = 10; printf("%d %d %d", a & b, a | b, a ^ b);
정답8 14 6

— 8 14 6. 12 = 1100, 10 = 1010 을 비트 단위로 AND · OR · XOR 한 값이다.

원리 — 비트 연산은 두 수를 2진수로 놓고 같은 자리끼리 계산한다. AND(&)는 둘 다 1 일 때만 1, OR(|)는 하나라도 1 이면 1, XOR(^)는 서로 다를 때만 1 이다. 12 는 8+4 = 1100, 10 은 8+2 = 1010 이다. 자리별로 보면 8의 자리는 둘 다 1, 4의 자리는 12 만, 2의 자리는 10 만, 1의 자리는 둘 다 0 이다. AND 는 8의 자리만 남아 8, OR 는 8+4+2 = 14, XOR 는 4+2 = 6 이다. 2진수로 바꿔 세로로 놓고 푸는 것이 가장 안 틀린다.

따라가기

  1. 12 = 1100, 10 = 1010
  2. AND: 1000 = 8
  3. OR: 1110 = 14
  4. XOR: 0110 = 6

헷갈리는 자리 — & 와 && 를 섞는 것. && 는 논리 AND 라 12 && 10 은 1 이다. 한 글자면 비트, 두 글자면 논리다. 그리고 XOR 는 "같은 수를 두 번 XOR 하면 원래대로" 라는 성질(a ^ b ^ b = a)이 있어 값 교환 · 암호 문제에 나온다.

이전 자료의 예상 문제다. 코드를 손으로 따라가고 docker gcc(alpine 13.2) 실행 결과와 대조했다.

예상 1회 · 7번Python

다음 코드의 출력을 쓰시오.

a = [x for x in range(1, 10) if x % 3 == 0] b = set(a + [3, 9, 12]) print(len(a), len(b), sum(a))
정답3 4 18

— 3 4 18. a 는 [3, 6, 9] 로 3개, 집합 b 는 중복이 사라져 {3, 6, 9, 12} 4개, a 의 합은 18 이다.

원리 — 리스트 컴프리헨션 [x for x in range(1, 10) if x % 3 == 0] 은 1~9 중 3 의 배수만 모은다 → [3, 6, 9]. a + [3, 9, 12] 는 리스트 이어 붙이기라 [3, 6, 9, 3, 9, 12] 인데, set() 에 넣으면 중복이 제거되어 {3, 6, 9, 12} 가 된다. 집합은 "중복 없음 · 순서 없음"이 성질이다. len(a) = 3, len(b) = 4, sum(a) = 3 + 6 + 9 = 18. range 의 끝 10 은 포함되지 않는다는 것도 확인 지점이다.

따라가기

  1. a = [3, 6, 9] → len 3
  2. a + [3, 9, 12] = [3, 6, 9, 3, 9, 12] → set → {3, 6, 9, 12} → len 4
  3. sum(a) = 18

헷갈리는 자리 — set 이 중복을 지운다는 것을 놓쳐 len(b) 를 6 으로 적는 것. 그리고 sum 의 대상이 a 인지 b 인지 — b 였다면 30 이다. 집합 문제는 "중복 제거 후 몇 개"가 늘 핵심이다.

이전 자료의 예상 문제다. 코드를 손으로 따라가고 Python 3.12 실행 결과와 대조했다.

예상 1회 · 8번Java

다음 코드의 출력을 쓰시오.

int a = 5, b = 3; System.out.println(a + b + "결과" + a + b);
정답8결과53

— 8결과53. + 는 왼쪽부터 계산되어 문자열을 만나기 전까지는 덧셈, 만난 뒤로는 이어 붙이기다.

원리 — 자바의 + 는 두 피연산자 중 하나라도 String 이면 문자열 연결이고, 둘 다 숫자면 덧셈이다. 그리고 같은 우선순위라 왼쪽에서 오른쪽으로 하나씩 계산한다. a + b 는 5 + 3 = 8(숫자). 8 + "결과" 는 "8결과"(문자열). "8결과" + a 는 "8결과5", 거기에 + b 는 "8결과53". 한 번 문자열이 되면 그 뒤의 숫자는 전부 붙는다. 같은 식이라도 괄호를 (a + b) 로 뒤에 두면 "8결과8" 이 된다.

따라가기

  1. 5 + 3 → 8
  2. 8 + "결과" → "8결과"
  3. "8결과" + 5 → "8결과5" → + 3 → 8결과53

헷갈리는 자리 — 뒤의 a + b 도 더해 "8결과8" 로 적는 것. 문자열 뒤에서는 더하기가 없다. println 이라 줄바꿈이 붙지만 답에는 안 적는다. 이 유형은 문자열 위치만 바꿔 여러 번 나온다 — 왼쪽부터 한 칸씩 계산하면 된다.

이전 자료의 예상 문제다. 코드를 손으로 따라가고 JDK 17 실행 결과와 대조했다.

예상 1회 · 9번C

다음 코드의 출력을 쓰시오.

int a[2][3] = {{1,2,3},{4,5,6}}; int *p = a[0]; printf("%d %d", *(p+4), a[1][0]);
정답5 4

— 5 4. 2차원 배열도 메모리에는 한 줄로 놓이므로, 첫 칸 주소에서 4칸 뒤는 두 번째 행의 두 번째 원소다.

원리 — int a[2][3] 은 "3칸짜리 행이 2개"이고 메모리에는 1 2 3 4 5 6 이 이어서 놓인다(행 우선). a[0] 은 첫 행의 시작 주소이므로 p = a[0] 은 1 이 있는 칸을 가리킨다. p + 4 는 int 4개 뒤, 곧 통째로 세었을 때 5번째(0부터 4번) 칸이라 값은 5 다. a[1][0] 은 두 번째 행의 첫 원소 4 다. 2차원 첨자 [i][j] 를 1차원 번호로 바꾸면 i × 열 수 + j 이고, 거꾸로 번호 4 는 4 ÷ 3 = 1 행, 4 % 3 = 1 열 → a[1][1] = 5 다.

따라가기

  1. 메모리 한 줄: [1 2 3 4 5 6], 번호 0~5
  2. p = a[0] → 번호 0. *(p + 4) → 번호 4 → 5
  3. a[1][0] → 1 × 3 + 0 = 번호 3 → 4
  4. 출력 5 4

헷갈리는 자리 — p 가 int * 이므로 p + 4 는 4바이트가 아니라 int 4개(16바이트)만큼 간다는 것. 그리고 a + 1 은 "행 하나(int 3개)" 를 건너뛰지만 a[0] + 1 은 "원소 하나"를 건너뛴다 — 같은 + 1 인데 타입에 따라 보폭이 다르다.

이전 자료의 예상 문제다. 코드를 손으로 따라가고 docker gcc(alpine 13.2) 실행 결과와 대조했다.

예상 1회 · 10번SQL

성적 테이블이 다음과 같을 때 쿼리 결과를 쓰시오.

이름 과목 점수 SELECT 이름, AVG(점수) AS 평균 김A 수학 90 FROM 성적 김A 영어 80 GROUP BY 이름 이B 수학 70 HAVING AVG(점수) > 85; 이B 영어 100 박C 수학 95
정답박C | 95

— 열 이름 · 평균, 한 행 박C · 95. 이름별 평균이 85 를 넘는 사람은 박C 뿐이다.

원리GROUP BY 이름 은 같은 이름의 행을 묶고, AVG(점수) 는 묶음마다 평균을 낸다. HAVING AVG(점수) > 85 는 묶은 뒤 평균에 거는 조건이라 85 는 안 들어간다(초과). 김A 는 (90 + 80) / 2 = 85, 이B 는 (70 + 100) / 2 = 85 라 둘 다 탈락이고 박C 는 95 하나뿐이라 평균 95 다.

따라가기

  1. 김A → 85(탈락), 이B → 85(탈락), 박C → 95
  2. HAVING > 85 → 박C 만
  3. 결과 박C | 95 (열 이름은 이름 · 평균)

헷갈리는 자리 — > 를 >= 로 읽어 세 사람을 다 남기는 것. "초과"와 "이상"은 다르다. 평균 열 이름을 AVG(점수)로 적는 것 — AS 평균 이 있으니 평균이다.

이전 자료의 예상 문제다. sqlite 3.49 에서 실행해 정답을 확인했다.

예상 1회 · 11번SQL

사용자 '김기사'에게 학생 테이블의 SELECT 권한을 부여하되, 김기사가 그 권한을 다른 사용자에게도 부여할 수 있도록 하는 SQL문을 작성하시오.

정답GRANT SELECT ON 학생 TO 김기사 WITH GRANT OPTION;

GRANT SELECT ON 학생 TO 김기사 WITH GRANT OPTION;

원리 — 권한을 주는 명령은 GRANT 권한 ON 대상 TO 사용자 다(DCL). 여기에 WITH GRANT OPTION 을 붙이면 받은 사람이 그 권한을 남에게 다시 줄 수 있다. 거두는 명령은 REVOKE 권한 ON 대상 FROM 사용자 이고, 남에게 넘겨준 권한까지 같이 거두려면 CASCADE 를 붙인다. GRANT 는 TO, REVOKE 는 FROM 이라는 짝을 기억한다.

따라가기

  1. 무슨 권한 → SELECT, 무엇에 → ON 학생
  2. 누구에게 → TO 김기사
  3. 다시 줄 수 있게 → WITH GRANT OPTION

헷갈리는 자리 — TO 를 FOR 나 FROM 으로 적는 것. WITH GRANT OPTION 을 빼면 "권한 부여"까지만 맞고 문제의 둘째 조건이 빠진다. 낱말 순서(권한 → ON → TO → WITH)가 곧 채점 기준이다.

이전 자료의 예상 문제다. sqlite 3.49 에서 실행해 정답을 확인했다.

예상 1회 · 12번DB

릴레이션 R(학번, 과목코드, 성적, 학생이름)에서 기본키는 (학번, 과목코드)이고 함수 종속이 다음과 같다: (학번, 과목코드) → 성적, 학번 → 학생이름. 이 릴레이션에 존재하는 함수 종속의 문제점과, 이를 제거하면 도달하는 정규형은?

정답부분 함수 종속 (학생이름이 복합키의 일부인 학번에만 종속) / 제거하면 제2정규형(2NF)

— 부분 함수 종속(학번 → 학생이름이 복합키의 일부에만 매달림), 제거하면 제2정규형.

원리 — 기본키가 (학번, 과목코드) 두 속성인데 학생이름은 학번만으로 정해진다. 키의 일부에만 매달린 이런 종속을 부분 함수 종속이라 하고, 이것이 있으면 같은 학생이 여러 과목을 들을 때 이름이 여러 행에 중복돼 갱신 이상이 난다. 부분 종속을 떼어 학생(학번, 학생이름) 표를 따로 만들면 제2정규형이 된다. 1NF → 2NF 의 조건이 바로 "부분 함수 종속 제거"다.

따라가기

  1. (학번, 과목코드) → 성적: 키 전체 종속, 정상
  2. 학번 → 학생이름: 키의 일부 → 부분 함수 종속
  3. 분리하면 → 2NF

헷갈리는 자리 — 이행 함수 종속이라 적는 것. 이행은 키 아닌 속성끼리(A → B → C)이고, 여기는 키의 일부가 결정자다. 정규형 사다리: 부분 종속 제거 = 2NF, 이행 종속 제거 = 3NF, 결정자가 후보키 = BCNF.

이전 자료의 예상 문제다. 교재 정의와 대조했다.

예상 1회 · 13번DB

트랜잭션의 특성: ① 연산이 전부 반영되거나 전혀 반영되지 않아야 한다 ② 성공적으로 완료된 트랜잭션의 결과는 시스템 장애가 발생해도 영구적으로 반영되어야 한다. 각각의 명칭은?

정답① 원자성 (Atomicity) ② 영속성/지속성 (Durability)

— ① 원자성(Atomicity) ② 영속성 · 지속성(Durability). 트랜잭션 네 특성(ACID) 중 둘이다.

원리 — 트랜잭션은 "전부 되거나 전부 안 되거나"여야 하는 일 묶음이다. 원자성 — 더 쪼갤 수 없는 하나처럼 전부 반영되거나 하나도 반영되지 않는다(송금에서 출금만 되고 입금이 안 되면 안 된다). 일관성 — 끝난 뒤 데이터베이스가 모순 없는 상태. 고립성 — 동시에 돌아도 서로 끼어들지 않은 것처럼. 영속성 — 완료(COMMIT)된 결과는 장애가 나도 남는다. 앞 글자 A · C · I · D 로 외운다.

따라가기

  1. 전부 반영 또는 전혀 반영 안 됨 → 원자성
  2. 완료 결과는 장애가 나도 영구 반영 → 영속성

헷갈리는 자리 — 영속성을 "일관성"으로 적는 것. "장애 · 영구"는 영속성, "모순 없음"은 일관성이다. 원자성의 표지는 "전부 아니면 전무(all or nothing)"다.

이전 자료의 예상 문제다. 교재 정의와 대조했다.

예상 1회 · 14번네트워크

200.1.1.0/24 네트워크를 4개의 동일한 서브넷으로 분할했다. ① 두 번째 서브넷의 브로드캐스트 주소 ② 각 서브넷에서 실제 호스트에 할당 가능한 IP 개수는?

정답① 200.1.1.127 ② 62개

— ① 200.1.1.127 ② 62개. /24 를 4개로 나누면 /26, 묶음 64 이라 둘째 묶음은 64~127 이고 호스트는 62 개다.

원리 — 4 = 2² 이니 네트워크 비트가 2 늘어 /26, 호스트 비트 6 → 묶음 64. 묶음은 0~63, 64~127, 128~191, 192~255. 둘째 서브넷의 브로드캐스트는 묶음의 마지막 주소 127 이고, 각 서브넷의 호스트 수는 64 − 2 = 62 다. "몇 개로 나누나"가 주어지면 2ⁿ 을 찾아 프리픽스에 n 을 더한다.

따라가기

  1. 4개 → /26 → 묶음 64
  2. 둘째 묶음 64~127 → 브로드캐스트 200.1.1.127
  3. 64 − 2 = 62

헷갈리는 자리 — 둘째 서브넷을 128~191 로 세는 것(첫째가 0~63 이다). 호스트 수 64 로 적는 것. 두 값이 짝이라 하나만 맞으면 부분 점수다.

이전 자료의 예상 문제다. ipaddress · 표준 정의로 확인했다.

예상 1회 · 15번네트워크

네트워크에 접속하는 호스트에게 IP 주소, 서브넷 마스크, 게이트웨이 주소 등을 자동으로 할당해 주는 프로토콜은? (영문 약자)

정답DHCP

— DHCP. 네트워크에 들어온 장비에게 IP · 서브넷 마스크 · 게이트웨이 · DNS 를 자동으로 빌려주는 프로토콜이다.

원리 — 장비마다 IP 를 손으로 적으면 중복과 실수가 난다. DHCP(Dynamic Host Configuration Protocol) 서버는 IP 주소 풀을 갖고, 새 장비가 브로드캐스트로 "주소 주세요"(Discover)를 외치면 서버가 제안(Offer) → 장비가 요청(Request) → 서버가 확인(Ack)하는 네 단계로 정해진 기간 동안 빌려준다(임대). RARP 가 하던 일을 넓힌 것이다. UDP 67 · 68 포트를 쓴다.

따라가기

  1. IP · 마스크 · 게이트웨이 자동 할당 → DHCP

헷갈리는 자리 — DNS(이름 → IP)와 섞는 것. DNS 는 이름을 풀고 DHCP 는 주소를 준다. "자동 할당 · 임대"가 DHCP 의 표지다.

이전 자료의 예상 문제다. 표준 정의와 대조했다.

예상 1회 · 16번보안

사용자가 올바른 도메인 주소를 입력해도 DNS 정보를 조작하여 가짜 사이트로 연결되게 하는 공격은?

정답파밍 (Pharming)

— 파밍(Pharming). 사용자가 주소를 맞게 쳐도 DNS 가 가짜 IP 를 돌려줘 가짜 사이트로 가게 하는 공격이다.

원리 — 피싱은 가짜 링크를 누르게 속이지만, 파밍은 이름을 IP 로 바꾸는 DNS 자체(서버나 PC 의 hosts 파일)를 조작해 올바른 주소로도 가짜로 간다. 사용자 잘못이 없어 더 위험하다. 이름은 farming(농사 짓듯 대량으로 거둔다)의 f 를 ph 로 바꾼 것이다. 막으려면 HTTPS 인증서를 확인하고 DNSSEC 을 쓴다.

따라가기

  1. 올바른 주소 입력 + DNS 조작 + 가짜 사이트 → 파밍

헷갈리는 자리 — 피싱(Phishing)과 바꾸는 것. "DNS 조작"이면 파밍, "속이는 메일 · 링크"면 피싱이다. 스미싱은 문자(SMS)로 하는 피싱이고, 보이스 피싱은 전화다 — 수단으로 이름이 갈린다.

이전 자료의 예상 문제다. 표준 정의와 대조했다.

예상 1회 · 17번보안

① 소인수분해의 어려움에 기반한 공개키(비대칭키) 암호 알고리즘 ② 타원 곡선 이론에 기반해 짧은 키로 동등한 보안 강도를 제공하는 공개키 알고리즘

정답① RSA ② ECC

— ① RSA ② ECC. 큰 수의 소인수분해가 어렵다는 것에 기댄 것이 RSA, 타원 곡선 위의 계산이 어렵다는 것에 기댄 것이 ECC 다.

원리 — 공개키 암호는 "한 방향은 쉽고 거꾸로는 어려운" 수학 문제 위에 세운다. RSA 는 두 큰 소수를 곱하기는 쉽지만 곱을 다시 소인수분해하기는 어렵다는 것을 쓴다 — 키가 2048비트 이상으로 길다. ECC(타원 곡선 암호)는 타원 곡선 위의 점 연산에서 거꾸로 푸는 것이 어렵다는 것을 써서, 256비트 키로 RSA 3072비트와 비슷한 강도를 낸다 — 짧은 키라 모바일 · 인증서에 널리 쓴다.

따라가기

  1. 소인수분해 → RSA
  2. 타원 곡선 · 짧은 키 · 같은 강도 → ECC

헷갈리는 자리 — DH(디피-헬만)는 이산대수 문제 위의 키 교환 방식이라 셋을 섞지 않는다. "소인수분해"는 RSA 하나뿐이다.

이전 자료의 예상 문제다. 표준 정의와 대조했다.

예상 1회 · 18번디자인패턴

① 복잡한 서브시스템들을 하나의 단순한 통합 인터페이스로 제공하여 사용을 쉽게 하는 구조 패턴 ② 객체의 상태를 저장해 두었다가 필요할 때 이전 상태로 복원(Undo)할 수 있게 하는 행위 패턴

정답① 퍼사드 (Facade) ② 메멘토 (Memento)

— ① 퍼사드(Facade) ② 메멘토(Memento). "통합 창구"와 "상태 저장 · 복원"이 각각의 표지다.

원리 — 퍼사드는 구조 패턴이다. 서브시스템 안에 클래스 열 개가 얽혀 있어도, 앞에 "간단한 메서드 몇 개"만 가진 퍼사드 클래스를 세우면 클라이언트는 그것만 부른다 — 건물 정면(facade)만 보고 안의 배관을 모르듯. 결합도가 내려가고 쓰기 쉬워진다. 메멘토는 행위 패턴이다. 객체의 내부 상태를 캡슐화를 깨지 않고 메멘토 객체에 저장해 두었다가, 나중에 그 메멘토로 되돌린다 — 편집기의 실행 취소(Undo), 게임 저장이 이것이다. 원본(Originator)이 메멘토를 만들고, 관리자(Caretaker)가 보관한다.

따라가기

  1. "복잡한 서브시스템을 단순한 통합 인터페이스로 · 구조" → 퍼사드
  2. "상태를 저장했다가 이전 상태로 복원(Undo) · 행위" → 메멘토

헷갈리는 자리 — 퍼사드를 어댑터로 적는 것. 어댑터는 인터페이스를 "변환"하고, 퍼사드는 여러 개를 "단순화"한다. 메멘토를 커맨드로 적는 것 — 커맨드도 Undo 를 하지만 "요청을 객체로" 만드는 것이 본질이고, 메멘토는 "상태 스냅샷"이 본질이다.

이전 자료의 예상 문제다. GoF 패턴 정의와 대조했다.

예상 1회 · 19번테스트

입력 유효 범위가 0~100인 프로그램을 테스트하기 위해 -1, 0, 100, 101을 테스트 데이터로 선정했다. 오류가 경계 부근에 집중된다는 점을 이용한 이 블랙박스 테스트 기법은?

정답경계값 분석 (Boundary Value Analysis)

— 경계값 분석(Boundary Value Analysis). 유효 범위 0~100 의 양 끝과 바로 바깥(−1 · 0 · 100 · 101)을 고른 것이 표지다.

원리 — 프로그래머의 실수는 경계에서 몰린다 — if (x < 100) 을 <= 로 써야 했다거나, 배열 크기를 하나 작게 잡았다거나. 그래서 동치 분할로 묶음을 나눈 뒤, 묶음의 가운데가 아니라 "경계와 그 바로 옆"을 검사하면 오류를 훨씬 잘 잡는다. 유효 범위가 0~100 이면 경계는 0 과 100 이고, 안쪽 값 0 · 100 과 바깥쪽 값 −1 · 101 을 고른다(2값 방식). 3값 방식이면 1 · 99 까지 더한다. 동치 분할이 "어디를 나눌까"라면 경계값 분석은 "나눈 선 위에서 검사하자"다.

따라가기

  1. 유효 범위 0~100 → 경계 0 · 100
  2. −1 · 0 · 100 · 101 → 경계 안팎 값
  3. "오류가 경계 부근에 집중" → 경계값 분석

헷갈리는 자리 — 동치 분할로 적는 것. 동치 분할은 50 같은 대표값을 고르고, 경계값은 0 · 100 · −1 · 101 을 고른다. 입력 데이터가 "경계 양옆"이면 경계값 분석이다. 두 기법은 보통 같이 쓴다 — 동치 분할로 묶음을 나누고, 각 묶음의 경계에서 경계값을 뽑는다. 그래서 문제가 둘 중 무엇을 묻는지는 "제시된 입력값이 대표값인가 경계값인가"로만 갈린다.

이전 자료의 예상 문제다. 표준 정의(ISTQB · V 모델)와 대조했다.

예상 1회 · 20번OS

교착상태(Deadlock) 발생 4가지 필요조건 중 빈칸 2개: 상호 배제, 점유와 대기, ( ① ), ( ② )

정답① 비선점 ② 환형 대기 (순환 대기)

— ① 비선점 ② 환형 대기(순환 대기). 교착상태 4조건은 상호 배제 · 점유와 대기 · 비선점 · 환형 대기다.

원리 — 교착상태(deadlock)는 여러 프로세스가 서로가 가진 자원을 기다리며 영원히 멈추는 것이다. 네 조건이 "동시에" 성립해야 생긴다. 상호 배제 — 자원을 한 번에 하나만 쓸 수 있다(프린터). 점유와 대기 — 이미 가진 것을 쥔 채로 다른 것을 기다린다. 비선점 — 남이 가진 자원을 강제로 뺏을 수 없다. 환형 대기 — A 는 B 것을, B 는 A 것을 기다리는 식으로 대기가 원을 이룬다. 넷 중 하나라도 깨면 교착이 안 생기므로 "예방"은 조건 하나를 없애는 것이고, "회피"는 은행원 알고리즘으로 안전 상태만 허용하는 것이다.

따라가기

  1. 주어진 둘: 상호 배제 · 점유와 대기
  2. 빠진 둘: 뺏을 수 없다 → 비선점, 기다림이 원을 이룬다 → 환형 대기

헷갈리는 자리 — 비선점을 "선점"으로 적는 것. 자원을 뺏을 수 있으면(선점) 교착이 풀리므로 교착 조건은 "비선점"이다. 환형 대기는 "순환 대기 · 원형 대기"로도 쓴다.

이전 자료의 예상 문제다. 표준 정의 · 손 계산으로 확인했다.

03채점 뒤 할 일

왜 이걸 하나 — 점수보다 어느 장으로 돌아가느냐가 결과다. 문항마다 가는 장이 정해져 있다.

코드 9문항 중 C 4 · Java 3 · Python 2 다. 1번은 오버라이딩된 cry 는 자식 것(D)이고 필드 name 은 참조형 것(Animal)이라 DAnimalAnimal, 8번은 5 + 3 이 먼저 8 이 되고 그 뒤로는 문자열 결합이라 8결과53 이다. 9번은 2차원 배열의 첫 행 주소에 4 를 더하면 두 번째 행 둘째 원소(5)다.

5번은 arr[3] 에서 예외가 나 그 줄 뒤의 print(arr[1]) 은 안 돌고 catch E · finally F · 그 뒤 G 다. 예외가 난 줄 다음부터 catch 까지가 건너뛰는 구간이라는 것을 표로 확인한다.

용어 11문항은 SQL·DB 4(HAVING 결과 · GRANT 작성 · 부분 함수 종속 · 원자성/영속성) · SW공학 3(퍼사드/메멘토 · 경계값 분석 · 교착상태 4조건) · 네트워크 2(/26 서브넷 · DHCP) · 보안 2(파밍 · RSA/ECC)다. 11번 GRANT 문은 WITH GRANT OPTION 까지 적어야 5점이다.

문항마다 가는 장

틀린 문항의 「가는 장」을 열면 그 문항이 04절에 그대로 있고, 앞뒤로 같은 꼴의 문항이 붙어 있다. 몰라서 틀렸으면 그 장 03절부터, 알았는데 틀렸으면 05절만 읽는다. 판정 기준은 E01 의 03절 표와 같다.

문항분야가는 장무엇을 묻나
1JavaE05오버라이딩 cry 와 가려진 필드 name
2CE02문자열 포인터 s + 6 · *(s + 2) · *s + 2
3CE03자릿수 합 재귀
4PythonE07sorted key=d.get 값 기준 정렬
5JavaE06배열 첨자 예외 뒤 finally 흐름
6CE04비트 & | ^ 계산
7PythonE07컴프리헨션과 집합 중복 제거
8JavaE06int + int + 문자열 결합 순서
9CE022차원 배열을 1차원 포인터로 *(p + 4)
10SQLE08GROUP BY + HAVING AVG 결과
11SQLE09GRANT … WITH GRANT OPTION 작성
12DBE10부분 함수 종속 제거 → 2NF
13DBE10원자성 · 영속성
14네트워크E12/26 두 번째 서브넷 브로드캐스트와 호스트 수
15네트워크E11IP 자동 할당 DHCP
16보안E14DNS 조작 파밍
17보안E13RSA 와 ECC
18디자인패턴E17퍼사드와 메멘토
19테스트E18경계값 분석
20OSE16교착상태 4조건 빈칸

이 회차에서 눈여겨볼 문항

문항왜 갈리나
1번a.sound() → cry() 는 Dog 것 D, name 은 Animal 의 것. a.name 도 참조형 Animal 의 것
9번a[0] 은 첫 행의 시작. p + 4 는 1 2 3 4 5 6 중 다섯째 → 5. a[1][0] 은 4
12번(학번, 과목코드) 가 키인데 학번 → 학생이름 이 있으면 부분 함수 종속이다. 떼어내면 2NF
14번/24 를 4개로 나누면 /26, 블록 64. 두 번째 서브넷은 64~127 이라 브로드캐스트 .127, 호스트 62

채점 결과는 오답노트에 회차별 점수와 틀린 문항 목록으로 모인다. 세트를 하나 끝낼 때마다 거기서 같은 장에서 반복해서 틀리는지를 본다. 두 세트에서 같은 장이 나오면 그 장의 06절 훈련을 그날 5문항 푼다.

04다음

왜 이걸 하나 — 세트의 결과는 다음에 열 장의 이름이어야 한다.