2024년 3회 실전 E24

LRU 20개 참조, 3중 서브쿼리, 연결 리스트 쌍 교환, 제네릭 오버로딩 B0. 손이 많이 가는 회차라 시간 배분을 시험한다

왜 이 장을 하나
2024년 3회는 한 문항에 시간이 오래 걸리는 것이 넷이다. 4번 LRU 20개 참조, 3번 3중 서브쿼리, 12번 연결 리스트, 18번 제네릭 오버로딩. 1회독에서 이 넷을 5분 안에 못 끝내면 비우고 2회독에 돌아오는 규칙을 실제로 지키는지가 이 세트의 시험이다.
다루는 것2024년 3회 20문항 · 복원본
시계150분 · 실제 시험과 같다
끝나면문항마다 가는 장이 표로 나온다

01푸는 법

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

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

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

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

02실전 세트

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

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

2024-3회 · 1번Java

다음 Java 프로그램의 실행 결과를 쓰시오.

public class Main { static String[] words = new String[3]; static void printWords(String[] values, int size) { for (int i = 1; i < size; i++) { System.out.print(values[i - 1].equals(values[i]) ? "O" : "N"); } for (String value : values) { System.out.print(value); } } public static void main(String[] args) { words[0] = "A"; words[1] = "A"; words[2] = new String("A"); printWords(words, 3); } }
정답OOAAA

— OOAAA. 이웃한 문자열을 equals 로 견주면 내용이 같아 두 번 다 O 이고, 이어서 세 원소 A 를 찍는다.

원리equals 는 글자 내용을 견주는 메서드라 new String("A") 로 만든 다른 객체여도 "A" 와 같다고 본다. 만약 == 였다면 셋째 원소는 다른 객체라 N 이 나왔을 것이다 — 이 문제는 그 차이를 반대로 찌른다. 첫 반복은 i = 1 부터 size − 1 까지 "앞 원소와 지금 원소"를 견주므로 두 번 돈다. 둘째 반복(향상된 for)은 배열의 원소를 순서대로 하나씩 꺼내 찍는다. 둘 다 print 라 한 줄이다.

따라가기

  1. words = ["A", "A", new "A"]
  2. i = 1: "A".equals("A") → O, i = 2: "A".equals(new "A") → O
  3. for 원소: A A A → 합쳐서 OOAAA

헷갈리는 자리 — 셋째 원소가 new 라서 둘째 비교를 N 으로 적는 것. equals 는 객체가 달라도 내용이 같으면 true 다. 첫 반복이 i = 0 이 아니라 1 부터라 비교가 두 번(원소 수 − 1)이라는 것도 놓치기 쉽다.

복원 코드를 JDK 17 로 컴파일 · 실행해 같은 출력을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2024-3회 · 2번Python

다음 Python 프로그램의 실행 결과를 쓰시오.

def reverse(values): for i in range(len(values) // 2): values[i], values[-i - 1] = values[-i - 1], values[i] values = [1, 2, 3, 4, 5, 6] reverse(values) print(sum(values[::2]) - sum(values[1::2]))
정답3

— 3. 리스트를 뒤집어 6 5 4 3 2 1 로 만든 뒤 짝수 첨자 값의 합에서 홀수 첨자 값의 합을 뺀다.

원리values[i], values[-i - 1] = values[-i - 1], values[i] 는 임시 변수 없이 두 칸을 맞바꾸는 파이썬 관용구다. 음수 첨자 −1 은 마지막, −2 는 그 앞이다. i 를 0 부터 길이의 절반(3)까지 돌리면 앞 3개와 뒤 3개가 짝지어 바뀌어 전체가 뒤집힌다. 리스트는 함수에 참조로 넘어가 원본이 바뀐다. [::2] 는 첨자 0 · 2 · 4, [1::2] 는 1 · 3 · 5 를 고른 것이다.

따라가기

  1. i = 0: 0번 ↔ −1번 → [6, 2, 3, 4, 5, 1]
  2. i = 1: 1번 ↔ −2번 → [6, 5, 3, 4, 2, 1]
  3. i = 2: 2번 ↔ −3번 → [6, 5, 4, 3, 2, 1]
  4. [::2] = 6, 4, 2 → 12. [1::2] = 5, 3, 1 → 9
  5. 12 − 9 = 3

헷갈리는 자리 — reverse 가 원본을 안 바꾼다고 보고 원래 순서로 계산하면 (1 + 3 + 5) − (2 + 4 + 6) = −3 이다. 리스트는 참조라 바뀐다. 슬라이스 스텝 2 를 "두 개씩"이 아니라 "두 번째부터"로 읽는 것도 흔하다.

복원 코드를 Python 3.12 로 실행해 같은 출력을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2024-3회 · 3번SQL

다음 테이블과 SQL문을 보고 출력값을 쓰시오.

employee

employee_idnameproject_id
1Alice10
2Bob10
3Charlie20
4Dana30
5Ethan30

project

project_idname
10Alpha
20Beta
30Gamma
SELECT COUNT(*) FROM employee AS e JOIN project AS p ON e.project_id = p.project_id WHERE p.name IN ( SELECT name FROM project WHERE project_id IN ( SELECT project_id FROM employee GROUP BY project_id HAVING COUNT(*) < 2 ) );
정답1

— 1. 인원이 2 명 미만인 프로젝트는 20(Beta)뿐이고, 그 프로젝트에 속한 직원은 Charlie 한 명이다.

원리 — 서브쿼리가 세 겹이면 가장 안쪽부터 값을 만들어 바깥으로 올린다. 가장 안쪽은 employee 를 project_id 별로 묶어(GROUP BY) 인원이 2 명 미만인(HAVING COUNT(*) < 2) project_id 를 고른다. 가운데는 그 id 의 프로젝트 이름을 뽑는다. 바깥은 employee 와 project 를 project_id 로 이어 붙인(JOIN … ON) 뒤 이름이 그 목록에 있는 행만 센다. 각 층의 결과를 표로 적어 두면 헷갈리지 않는다.

따라가기

  1. 안쪽: 10 → 2명, 20 → 1명, 30 → 2명 → COUNT < 2 인 것은 20
  2. 가운데: project_id 20 의 name → Beta
  3. 바깥: JOIN 뒤 p.name = Beta 인 행 → Charlie 한 행 → 1

헷갈리는 자리 — HAVING COUNT(*) < 2 를 "2 명 이하"로 읽어 10 · 30 까지 넣는 것(<= 가 아니다). JOIN 결과가 다섯 행인 것을 답으로 적는 것 — WHERE 가 거른 뒤의 수다. 층마다 무엇을 돌려주는지(id → 이름 → 행 수)를 적는다.

표를 sqlite 3.49 에 넣고 같은 SQL 을 실행해 결과를 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 표 · 값이 실제와 다를 수 있다.

2024-3회 · 4번OS

프레임이 3개일 때 다음 참조열을 LRU로 처리한 페이지 부재 횟수를 쓰시오.

7, 0, 1, 2, 0, 3, 0, 4, 2, 3, 0, 3, 2, 1, 2, 0, 1, 7, 0, 1

정답12회

— 12회. 교과서의 대표 참조열로, 프레임 3개 LRU 는 20번 참조 중 12번 부재 · 8번 적중이다.

원리 — LRU 는 프레임이 꽉 찼을 때 "가장 오래전에 쓰인" 페이지를 내보낸다. 따라가는 요령은 각 프레임에 "마지막으로 쓰인 시각"을 적어 두는 것이다 — 적중이면 그 페이지의 시각만 갱신하고, 부재면 시각이 가장 옛날인 페이지를 빼고 새 페이지를 넣는다. 처음 3개는 빈 프레임을 채우는 부재다. 실수는 대개 "적중했을 때 시각 갱신을 안 해서" 생기니 적중도 표에 반드시 적는다.

따라가기

  1. 7 · 0 · 1 → 부재 3, [7 0 1]
  2. 2 → 부재(7 out) [2 0 1] · 0 적중 · 3 → 부재(1 out) [2 0 3] · 0 적중
  3. 4 → 부재(2 out) [4 0 3] · 2 → 부재(3 out) [4 0 2] · 3 → 부재(0 out) [4 3 2] · 0 → 부재(4 out) [0 3 2]
  4. 3 · 2 → 적중 · 1 → 부재(0 out) [1 3 2] · 2 적중 · 0 → 부재(3 out) [1 0 2] · 1 적중
  5. 7 → 부재(2 out) [1 0 7] · 0 · 1 → 적중
  6. 부재 3+2+4+2+1 = 12회

헷갈리는 자리 — 같은 참조열을 FIFO 로 돌리면 15회, OPT(최적)로 돌리면 9회다. 방식을 잘못 읽으면 숫자가 통째로 달라진다. 3단계에서 "0 이 최근에 쓰였는데 왜 나가나"가 걸린다 — 시각을 적어 보면 4(8번째) · 3(6번째) · 0(7번째) 중 3 이 먼저 나가고, 그다음 0(7번째)이 4(8번째) · 2(9번째)보다 오래돼 나간다.

표를 손으로 한 칸씩 채우고 파이썬 시뮬레이션 스크립트로 같은 값을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 표 · 값이 실제와 다를 수 있다.

2024-3회 · 5번보안

출발지 IP를 피해자 주소로 위조한 ICMP Echo 요청을 브로드캐스트하여, 다수의 응답이 피해자에게 몰리게 하는 DDoS 공격은?

정답스머핑 (Smurfing / Smurf Attack)

— 스머핑(Smurfing, Smurf Attack). 출발지를 피해자로 속인 ping 을 네트워크 전체에 뿌려 수많은 응답이 피해자에게 쏟아지게 하는 공격이다.

원리 — ICMP 에코 요청(ping)을 브로드캐스트 주소로 보내면 그 망의 모든 컴퓨터가 응답한다. 공격자가 요청의 출발지 IP 를 피해자 주소로 위조하면 그 응답이 전부 피해자에게 간다 — 한 패킷이 수백 개로 불어나는 증폭 공격이다. 막으려면 라우터가 브로드캐스트로 오는 ICMP 를 버리면 된다. "ICMP · 브로드캐스트 · 출발지 위조 · 응답 폭주"가 표지다.

따라가기

  1. 출발지를 피해자로 위조한 ICMP Echo → 브로드캐스트 → 응답 집중 → 스머핑

헷갈리는 자리 — Ping of Death(너무 큰 ICMP 패킷 하나)나 SYN 플러딩(TCP)과 섞는 것. 브로드캐스트로 "여럿이 응답"하게 만드는 것이 스머핑이다. UDP 로 같은 짓을 하면 Fraggle 이다.

표준 · 교재 정의(공격 기법 · 보안 솔루션 · 시큐어 코딩 가이드)와 대조했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장이 실제와 다를 수 있다.

2024-3회 · 6번C

다음 C 프로그램의 실행 결과를 쓰시오.

#include <stdio.h> int next_value(void) { static int value = 0; value += 2; return value; } int main(void) { int value = 1; int sum = 0; for (int i = 0; i < 4; i++) { value++; sum += next_value(); } printf("%d", sum); return 0; }
정답20

— 20. static 변수는 함수가 끝나도 값을 기억하므로 next_value 는 2 · 4 · 6 · 8 을 차례로 돌려주고, 그 합이 20 이다.

원리 — 보통의 지역 변수는 함수가 불릴 때 새로 만들어지고 끝나면 사라진다. 그런데 static int value = 0 은 프로그램이 시작할 때 한 번만 0 으로 만들어지고, 함수가 끝나도 지워지지 않고 다음 호출 때 이어서 쓴다. 그래서 부를 때마다 2 씩 쌓인다. main 에도 value 라는 변수가 있지만 이름만 같을 뿐 다른 칸이고, main 의 value 는 어디에도 쓰이지 않는 미끼다.

따라가기

  1. 1회: value 0 → 2, 돌려줌 2, sum 2
  2. 2회: 2 → 4, sum 6
  3. 3회: 4 → 6, sum 12
  4. 4회: 6 → 8, sum 20

헷갈리는 자리 — static 을 무시해 매번 0 에서 시작한다고 보면 2 × 4 = 8 이 된다. main 의 value++ 를 함수 안 value 와 섞어 계산하는 것 — 이름이 같아도 범위가 다르면 남남이다. 함수 안의 value 는 밖에서 볼 수도 없다.

복원 코드를 gcc(도커 alpine)로 실행해 같은 출력을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2024-3회 · 7번네트워크

공중망(인터넷)을 마치 전용선처럼 사용할 수 있게 하는 암호화 기반 가상 사설 네트워크는? (영문 약자)

정답VPN

— VPN. 공용 인터넷 위에 암호화된 터널을 뚫어 전용선처럼 쓰는 가상 사설망이다.

원리 — 지사와 본사를 잇는 전용선은 안전하지만 비싸다. VPN(Virtual Private Network)은 값싼 공중망(인터넷)을 쓰되 양 끝에서 데이터를 암호화하고 캡슐에 싸서(터널링) 보내므로 중간에서 봐도 내용을 모른다. 터널링 프로토콜로 PPTP · L2TP · IPSec 등이 쓰이고, 2023년 1회 6번의 L2TP 가 그중 하나다. "공중망을 전용선처럼 · 암호화 · 가상 사설"이 표지다.

따라가기

  1. 공중망 + 암호화 터널 + 전용선처럼 → VPN

헷갈리는 자리 — VLAN(스위치 안에서 논리적으로 망을 나누는 것)과 섞는 것. VLAN 은 분리, VPN 은 원격 연결이다. 영문 약자를 요구하니 풀어 쓴 이름은 답이 아니다.

표준 · 교재 정의(OSI · TCP/IP · IEEE · RFC 용어)와 대조했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장이 실제와 다를 수 있다.

2024-3회 · 8번디자인패턴

GoF 디자인 패턴 중 객체 간의 상호작용과 책임 분배를 다루는 패턴 분류는? (Observer, Strategy, Command 등이 속함)

정답행위 (Behavioral) 패턴

— 행위(Behavioral) 패턴. GoF 23개는 생성 5 · 구조 7 · 행위 11 로 나뉘고, 객체 사이의 상호작용과 책임 분배를 다루는 것이 행위 패턴이다.

원리 — 생성 패턴은 "객체를 어떻게 만드나"(싱글톤 · 팩토리 메서드 · 추상 팩토리 · 빌더 · 프로토타입). 구조 패턴은 "클래스 · 객체를 어떻게 조립하나"(어댑터 · 브리지 · 컴포지트 · 데코레이터 · 퍼사드 · 플라이웨이트 · 프록시). 행위 패턴은 "객체들이 어떻게 협력하고 누가 무엇을 책임지나"(옵저버 · 스트래티지 · 커맨드 · 이터레이터 · 템플릿 메서드 · 스테이트 · 비지터 · 미디에이터 · 메멘토 · 책임 연쇄 · 인터프리터). 행위 패턴이 11개로 가장 많고, 이름에 동작(관찰 · 명령 · 전략 · 방문)이 들어간다.

따라가기

  1. "객체 간의 상호작용과 책임 분배" → 만드는 것도 조립도 아닌 "협력"
  2. 예시 Observer · Strategy · Command 는 전부 행위 → 행위 패턴

헷갈리는 자리 — 세 분류의 개수 5 · 7 · 11 과 소속을 외운다. 자주 틀리는 소속 — 프록시 · 데코레이터 · 퍼사드는 구조, 템플릿 메서드 · 스테이트 · 이터레이터는 행위, 빌더 · 프로토타입은 생성이다. 영문 Behavioral 철자도 같이 적어 둔다.

GoF 책 Design Patterns 의 패턴 정의(생성 5 · 구조 7 · 행위 11)와 대조했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장이 실제와 다를 수 있다.

2024-3회 · 9번테스트

커버리지 매칭: ① 모든 문장을 최소 1회 실행 ② 모든 분기의 참/거짓을 각 1회 ③ 개별 조건식의 참/거짓을 각 1회

정답① 문장(구문) 커버리지 ② 분기(결정) 커버리지 ③ 조건 커버리지

— ① 문장(구문) 커버리지 ② 분기(결정) 커버리지 ③ 조건 커버리지. "문장 실행 · 분기 참/거짓 · 개별 조건 참/거짓"이 각각의 정의다.

원리 — 화이트박스 커버리지는 "코드의 무엇을 얼마나 밟았나"를 재는 자다. 문장 커버리지는 모든 문장(줄)이 한 번은 실행됐나 — 가장 약하다. if 문의 참 쪽 문장만 실행해도 만족하니 거짓 쪽은 안 봐도 된다. 분기 커버리지는 모든 분기의 참 · 거짓을 각각 밟았나 — if 의 두 갈래를 다 가야 한다. 조건 커버리지는 분기 안의 개별 조건식(A · B)이 각각 참 · 거짓을 가졌나 — 갈래가 아니라 조건 하나하나를 본다. 세 개는 강도 순서의 앞부분이고, 뒤에 조건/결정 → MC/DC → 다중 조건이 이어진다.

따라가기

  1. "모든 문장을 최소 1회 실행" → 문장(구문) 커버리지
  2. "모든 분기의 참/거짓을 각 1회" → 분기(결정) 커버리지
  3. "개별 조건식의 참/거짓을 각 1회" → 조건 커버리지

헷갈리는 자리 — ②와 ③을 바꾸는 것. "분기 · 결정"은 if 전체의 결과이고 "조건"은 && 로 묶인 낱개다. 문장에 "개별 조건식"이 보이면 조건, "분기"가 보이면 분기다. 결정 커버리지 = 분기 커버리지, 구문 커버리지 = 문장 커버리지로 이름이 둘씩이다.

표준 · 교재 정의(ISTQB 용어 · 커버리지 6단계 · 테스트 수준)와 대조했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장이 실제와 다를 수 있다.

2024-3회 · 10번DB

키 매칭: ① 다른 릴레이션의 기본키를 참조 ② 유일성+최소성을 만족 ③ 후보키 중 기본키를 제외한 나머지 ④ 유일성만 만족하는 속성 집합

정답① 외래키 ② 후보키 ③ 대체키 ④ 슈퍼키

— ① 외래키 ② 후보키 ③ 대체키 ④ 슈퍼키. 키의 정의를 "유일성 · 최소성"으로 가르는 문제다.

원리 — 키는 행을 구별해 주는 속성(들)이다. 유일성(같은 값의 행이 없음)만 있으면 슈퍼키 — 필요 없는 속성을 더 끼워도 여전히 슈퍼키다. 유일성에 최소성(하나라도 빼면 유일하지 않음)까지 갖추면 후보키. 후보키 중 하나를 골라 대표로 삼은 것이 기본키, 뽑히지 않은 나머지 후보키가 대체키. 다른 표의 기본키를 가리키는 속성이 외래키다. "유일 + 최소 = 후보, 후보 중 대표 = 기본, 나머지 = 대체, 유일만 = 슈퍼"로 줄인다.

따라가기

  1. 다른 릴레이션의 기본키를 참조 → 외래키
  2. 유일성 + 최소성 → 후보키
  3. 후보키 중 기본키를 뺀 나머지 → 대체키
  4. 유일성만 → 슈퍼키

헷갈리는 자리 — 슈퍼키와 후보키를 바꾸는 것. 최소성이 있으면 후보, 없으면 슈퍼다. 대체키를 "대신 쓰는 키"로 읽어 외래키와 섞는 것 — 대체키는 기본키 후보 중 탈락한 것이다.

교재 정의(관계형 모델 · 정규화 · 트랜잭션 표준 용어)와 대조했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장이 실제와 다를 수 있다.

2024-3회 · 11번

URI 구성요소를 설명과 매칭하시오: 스킴(프로토콜) / 권한(사용자정보·호스트·포트) / 경로 / 쿼리 스트링 / 프래그먼트

정답스킴 → 권한(호스트:포트) → 경로 → 쿼리 스트링 → 프래그먼트 순서 매칭

— 스킴 → 권한(사용자정보 · 호스트 · 포트) → 경로 → 쿼리 스트링 → 프래그먼트. URI 를 왼쪽부터 오른쪽으로 읽는 순서 그대로다.

원리 — URI 는 "어떤 방법으로(스킴), 어느 서버의(권한), 어느 자원을(경로), 어떤 조건으로(쿼리), 그중 어디를(프래그먼트)" 가리키는 한 줄이다. 예로 https://user:pw@www.example.com:8080/board/list?page=2&sort=new#top 을 뜯으면 — https 가 스킴(프로토콜), user:pw@www.example.com:8080 이 권한(authority: 사용자정보@호스트:포트), /board/list 가 경로, ?page=2&sort=new 가 쿼리 스트링(키=값을 &로 연결), #top 이 프래그먼트(문서 안 위치, 서버에 안 보내고 브라우저만 쓴다)다. 구분 기호 :// · @ · : · / · ? · # 이 각 부분의 경계다.

따라가기

  1. :// 앞 → 스킴(https)
  2. :// 뒤 첫 / 앞 → 권한(사용자정보@호스트:포트)
  3. / 부터 ? 앞 → 경로
  4. ? 뒤 → 쿼리 스트링(key=value&key=value)
  5. # 뒤 → 프래그먼트

헷갈리는 자리 — 프래그먼트를 서버로 보내는 것으로 아는 것. # 뒤는 브라우저가 페이지 안에서 스크롤할 위치라 HTTP 요청에 포함되지 않는다. 그리고 URL 과 URI 의 관계 — URL(위치)은 URI(식별자)의 부분집합이다. 시험은 "권한(authority)"이라는 이름을 낯설어해 틀린다 — 호스트 · 포트가 든 부분이 권한이다.

표준 · 교재 정의(운영체제 · 리눅스 명령 · 웹 · 클라우드 용어)와 대조했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장이 실제와 다를 수 있다.

2024-3회 · 12번C

다음 C 프로그램의 실행 결과를 쓰시오.

#include <stdio.h> struct Node { int value; struct Node *next; }; void swap_pairs(struct Node *node) { while (node != NULL && node->next != NULL) { int temp = node->value; node->value = node->next->value; node->next->value = temp; node = node->next->next; } } int main(void) { struct Node n1 = { 1, NULL }; struct Node n2 = { 2, NULL }; struct Node n3 = { 3, NULL }; n1.next = &n3; n3.next = &n2; swap_pairs(&n1); for (struct Node *p = &n1; p != NULL; p = p->next) { printf("%d", p->value); } return 0; }
정답312

— 312. 리스트가 1 → 3 → 2 순서로 이어져 있고, 앞의 두 노드 값만 맞바꾼 뒤 순서대로 찍는다.

원리 — 연결 리스트는 각 노드가 "다음 노드의 주소"를 들고 있는 사슬이다. 순서는 변수 이름(n1 · n2 · n3)이 아니라 next 가 정한다. n1.next = &n3; n3.next = &n2 이므로 사슬은 n1 → n3 → n2 → NULL 이다. swap_pairs 는 두 노드씩 짝지어 값(value)만 맞바꾸고, node = node->next->next 로 두 칸 건너뛴다. 남은 노드가 하나뿐이면(node->next == NULL) 짝이 없어 그대로 둔다. 사슬(next)은 안 바뀌고 값만 바뀐다는 것이 핵심이다.

따라가기

  1. 사슬: n1(1) → n3(3) → n2(2) → NULL
  2. node = n1: n1 과 n3 의 값 교환 → n1 = 3, n3 = 1. node = n3->next = n2
  3. node = n2: n2->next 가 NULL → 반복 끝(값 그대로 2)
  4. 출력: n1(3) → n3(1) → n2(2) → 312

헷갈리는 자리 — 사슬을 n1 → n2 → n3 으로 넘겨짚고 213 이라 적는 것. 초기화의 NULL 은 나중 대입으로 덮였으니 마지막 대입 두 줄이 사슬이다. 또 "교환"을 노드 순서 교환으로 읽는 것 — 이 코드는 값만 바꾼다.

복원 코드를 gcc(도커 alpine)로 실행해 같은 출력을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2024-3회 · 13번DB

"기본키는 NULL 값을 가질 수 없고 중복될 수 없다"는 무결성 제약조건은?

정답개체 무결성

— 개체 무결성. 기본키에 NULL 과 중복을 금하는 규칙이다.

원리 — 기본키는 행을 찾는 이름표다. 이름표가 비어 있으면(NULL) 그 행을 가리킬 수 없고, 두 행이 같은 이름표를 달면 어느 것인지 알 수 없다. 그래서 기본키는 NULL 도 중복도 안 된다는 규칙에 "개체(행) 하나하나를 온전히 구별한다"는 뜻으로 개체 무결성이라는 이름이 붙었다. 외래키 쪽 규칙은 참조 무결성, 값 범위 규칙은 도메인 무결성이다.

따라가기

  1. "기본키는" → 개체 쪽 규칙
  2. "NULL 불가 · 중복 불가" → 개체 무결성

헷갈리는 자리 — 참조 무결성이라 적는 것 — 그것은 외래키 이야기다. "기본키"가 나오면 개체, "외래키"가 나오면 참조라는 두 낱말만 잡으면 된다. 세 무결성을 한 문제에 다 묻는 꼴(2025년 1회 2번)도 있다.

교재 정의(관계형 모델 · 정규화 · 트랜잭션 표준 용어)와 대조했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장이 실제와 다를 수 있다.

2024-3회 · 14번Python

다음 Python 프로그램의 실행 결과를 쓰시오.

def value_of(value): if type(value) == type(100): return 100 elif type(value) == type(""): return len(value) return 20 a = "100.0" b = 100.0 c = (100, 200) print(value_of(a) + value_of(b) + value_of(c))
정답45

— 45. "100.0" 은 문자열이라 길이 5, 100.0 은 실수라 20, 튜플도 20 이다.

원리type(x) 는 값의 자료형을 돌려준다. type(100) 은 int, type("") 은 str 이다. 따옴표로 감싼 "100.0" 은 숫자처럼 보여도 문자열이고, 100.0 은 소수점이 있어 float, (100, 200) 은 튜플이다. 함수는 int 면 100, str 이면 글자 수, 그 밖은 20 을 돌려준다. 세 값 중 int 는 하나도 없다는 것이 이 문제의 핵심이다.

따라가기

  1. a = "100.0" → str → len = 5 (1, 0, 0, ., 0)
  2. b = 100.0 → float → int 도 str 도 아님 → 20
  3. c = (100, 200) → tuple → 20
  4. 5 + 20 + 20 = 45

헷갈리는 자리 — "100.0" 을 숫자로 보고 100 을 더하는 것. 따옴표가 있으면 문자열이다. 100.0 을 int 로 보는 것 — float 는 int 와 다른 자료형이다. 길이를 셀 때 소수점도 한 글자다(4 가 아니라 5).

복원 코드를 Python 3.12 로 실행해 같은 출력을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2024-3회 · 15번UML

UML 관계 매칭: ① 클래스가 다른 클래스를 사용하는 지속적 연결(실선) ② 상속 관계(빈 삼각형) ③ 일시적으로 사용하는 관계(점선 화살표)

정답① 연관(Association) ② 일반화(Generalization) ③ 의존(Dependency)

— ① 연관(Association) ② 일반화(Generalization) ③ 의존(Dependency). 실선 · 빈 삼각형 · 점선 화살표가 각각의 표지다.

원리 — UML 클래스 사이 관계는 선 모양으로 구분한다. 연관은 실선 — 한 클래스가 다른 클래스를 필드로 갖고 오래 쓴다(학생 – 강의). 일반화는 실선 + 빈 삼각형 — 상속, 자식이 부모를 가리킨다(개 → 동물). 의존은 점선 화살표 — 메서드 인자나 지역 변수로 잠깐 쓴다(주문이 계산기를 한 번 부른다). 여기에 집합(빈 마름모) · 합성(채운 마름모) · 실체화(점선 + 빈 삼각형)까지 합쳐 6종이다. "얼마나 오래, 얼마나 강하게 붙어 있나"가 선의 진하기와 모양으로 드러난다.

따라가기

  1. "지속적 연결(실선)" → 연관
  2. "상속(빈 삼각형)" → 일반화
  3. "일시적 사용(점선 화살표)" → 의존

헷갈리는 자리 — 의존과 연관을 바꾸는 것. 필드로 갖고 있으면 연관(실선), 인자로 받아 잠깐 쓰면 의존(점선). 그리고 실체화도 점선인데 끝이 빈 삼각형이면 실체화(인터페이스 구현), 열린 화살촉이면 의존이다.

표준 · 교재 정의(결합도 6단계 · 응집도 7단계 · UML 2.x 다이어그램 분류)와 대조했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장이 실제와 다를 수 있다.

2024-3회 · 16번Java

다음 Java 프로그램의 실행 결과를 쓰시오.

public class Main { public static void main(String[] args) { int sum = 0; try { fail(); } catch (NullPointerException e) { sum += 1; } catch (Exception e) { sum += 10; } finally { sum += 100; } System.out.print(sum); } static void fail() throws Exception { throw new NullPointerException(); } }
정답101

— 101. 던져진 NullPointerException 은 첫 catch 에 잡혀 1 이 더해지고, finally 는 항상 돌아 100 이 더해진다.

원리 — try 안에서 예외가 나면 그 아래 줄은 건너뛰고 catch 들을 위에서부터 훑어 "예외 종류에 맞는 첫 catch" 하나만 실행한다. NullPointerException 은 Exception 의 자식이라 둘째 catch(Exception)에도 맞지만, 이미 첫 catch 에 잡혔으니 둘째는 안 돈다. finally 는 예외가 났든 안 났든, 잡혔든 안 잡혔든 마지막에 반드시 돈다. fail() 의 throws Exception 은 "이 메서드가 예외를 던질 수 있다"는 선언일 뿐 무엇을 던지는지는 몸통이 정한다.

따라가기

  1. fail() → NullPointerException 던짐
  2. catch (NullPointerException) 에 잡힘 → sum = 1
  3. catch (Exception) 은 건너뜀
  4. finally → sum = 101 → 101

헷갈리는 자리 — 둘째 catch 도 맞으니 10 을 더해 111 로 적는 것. catch 는 하나만 잡는다. finally 를 빼고 1 로 적는 것 — finally 는 언제나 돈다. catch 순서가 반대(Exception 이 먼저)였다면 컴파일 오류다 — 넓은 것이 위에 오면 아래는 닿을 수 없기 때문이다.

복원 코드를 JDK 17 로 컴파일 · 실행해 같은 출력을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2024-3회 · 17번Java

다음 Java 프로그램의 실행 결과를 쓰시오.

public class Main { public static void main(String[] args) { Base a = new Derived(); Derived b = new Derived(); System.out.print(a.getX() + a.x + b.getX() + b.x); } } class Base { int x = 3; int getX() { return x * 2; } } class Derived extends Base { int x = 7; int getX() { return x * 3; } }
정답52

— 52. 메서드는 실제 객체(Derived)를 따라 둘 다 21 이고, 필드 x 는 변수의 겉모양을 따라 a.x 는 3, b.x 는 7 이다.

원리 — 자바에서 메서드는 갈아끼워지지만(오버라이딩) 필드는 갈아끼워지지 않는다. 부모와 자식이 같은 이름의 필드를 가지면 객체 안에 두 칸이 나란히 있고, 어느 칸을 읽을지는 변수의 선언 타입이 정한다. Base a = new Derived() 에서 a.x 는 Base 의 칸(3), a.getX() 는 Derived 의 메서드(그 안의 x 는 Derived 칸 7 → 21). Derived b 는 겉도 Derived 라 b.x 가 7 이다. 한 문제 안에 겉 · 속 규칙을 두 번 넣은 것이다.

따라가기

  1. a.getX() → Derived.getX → 7 × 3 = 21
  2. a.x → 겉이 Base → 3
  3. b.getX() → 21, b.x → 겉이 Derived → 7
  4. 21 + 3 + 21 + 7 = 52

헷갈리는 자리 — a.x 를 7 로 읽어 56 을 적는 것. 필드는 겉을 따른다. a.getX() 를 Base 판(3 × 2 = 6)으로 읽어 37 로 적는 것 — 메서드는 속을 따른다. 두 규칙을 반대로 적용하면 정확히 틀린다.

복원 코드를 JDK 17 로 컴파일 · 실행해 같은 출력을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2024-3회 · 18번Java

다음 Java 프로그램의 실행 결과를 쓰시오.

class Printer { void print(Integer value) { System.out.print("A" + value); } void print(Object value) { System.out.print("B" + value); } void print(Number value) { System.out.print("C" + value); } } public class Main { static class Collection<T> { T value; Collection(T value) { this.value = value; } void print() { new Printer().print(value); } } public static void main(String[] args) { new Collection<>(0).print(); } }
정답B0

— B0. 제네릭 T 는 컴파일 때 Object 로 지워지므로 print(value)print(Object) 로 묶이고, 값 0 이 뒤에 붙는다.

원리 — 오버로딩(같은 이름 · 다른 매개변수 타입) 중 어느 것을 부를지는 실행 때가 아니라 컴파일 때 "변수의 선언 타입"으로 정해진다. Collection<T> 안의 value 는 타입이 T 인데, 자바의 제네릭은 컴파일이 끝나면 T 를 그 상한인 Object 로 바꿔 버린다(타입 소거). 그래서 new Printer().print(value) 는 실제로 Integer 0 이 들어 있어도 print(Object) 로 묶인다. 실행 때 "더 잘 맞는 Integer 판"으로 바꿔 타는 일은 없다 — 오버로딩은 정적이다.

따라가기

  1. new Collection<>(0) → T 는 Integer 로 추론되지만 코드 안에서는 Object 취급
  2. print(value) → 컴파일 때 print(Object) 선택
  3. "B" + 0 → B0

헷갈리는 자리 — 값이 정수니 print(Integer) 의 A0, 혹은 Number 라서 C0 을 적는 것. 오버로딩 선택은 값이 아니라 선언 타입으로, 그것도 컴파일 때 끝난다. 2026년 1회 1번(f(Object) 와 f(String))이 같은 규칙의 다른 얼굴이다.

복원 코드를 JDK 17 로 컴파일 · 실행해 같은 출력을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2024-3회 · 19번C

다음 C 프로그램의 실행 결과를 쓰시오.

#include <stdio.h> void update(int **values, int size) { for (int i = 0; i < size; i++) { *(*values + i) = (*(*values + i) + i) % size; } } int main(void) { int values[] = { 3, 1, 4, 1, 5 }; int *p = values; int **pp = &p; update(pp, 5); printf("%d", values[2]); return 0; }
정답1

— 1. update 가 배열을 제자리에서 (값 + 첨자) % 5 로 바꾸고, 셋째 칸은 (4 + 2) % 5 = 1 이 된다.

원리int *p = values 는 배열 첫 칸의 주소를 적은 쪽지 p, int **pp = &p 는 "쪽지 p 가 놓인 자리"의 주소를 적은 두 번째 쪽지다. 그래서 *values(함수 안 이름)는 쪽지 p 의 내용, 즉 배열 첫 칸의 주소이고, *values + i 는 그 주소에서 i 칸 옆, 앞에 별표를 하나 더 붙인 *(*values + i) 가 그 칸의 값이다. 이중 포인터라 해도 결국 두 번 따라가서 원래 배열의 칸에 도착한다는 것이 전부다. 복사본이 아니라 원래 배열을 고치므로 main 의 values 가 바뀐다.

따라가기 — values = [3, 1, 4, 1, 5], size = 5. 새 값 = (옛 값 + i) % 5.

  1. i = 0: (3 + 0) % 5 = 3
  2. i = 1: (1 + 1) % 5 = 2
  3. i = 2: (4 + 2) % 5 = 6 % 5 = 1
  4. i = 3: (1 + 3) % 5 = 4, i = 4: (5 + 4) % 5 = 4 → 배열 [3, 2, 1, 4, 4]
  5. values[2]1

헷갈리는 자리 — 별표 두 개를 보고 겁먹어 값을 안 바꾸는 함수라고 여기는 것. 주소를 넘겼으니 원본이 바뀐다. 또 % 를 빼먹고 6 을 적는 것 — 나머지 연산이 마지막에 있다.

복원 코드를 gcc(도커 alpine)로 실행해 같은 출력을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2024-3회 · 20번네트워크

고정된 기반 시설(AP, 라우터) 없이 이동 노드들끼리 자율적으로 망을 구성하는 네트워크로, 재난 현장·군사 작전에 활용되는 것은?

정답애드혹 네트워크 (Ad-hoc Network)

— 애드혹 네트워크(Ad-hoc Network). 기지국 · AP 없이 단말끼리 직접 이어 만드는 임시 망이다.

원리 — 보통의 무선망은 AP 나 기지국이 가운데서 중계한다(인프라스트럭처 모드). 애드혹(ad hoc, "그때그때")은 그런 기반 시설 없이 노드들이 서로 라우터 역할을 하며 다중 홉으로 망을 만든다. 노드가 움직여 토폴로지가 계속 바뀌므로 재난 현장 · 군사 작전 · 차량 간 통신처럼 기반 시설을 세울 수 없는 곳에 쓴다. 이동 노드로 만든 것을 MANET 이라고도 부른다.

따라가기

  1. 기반 시설 없이 · 노드끼리 자율 구성 · 재난 · 군사 → 애드혹 네트워크

헷갈리는 자리 — 메시 네트워크와 섞는 것. 메시는 고정 노드가 그물처럼 연결된 구조이고, 애드혹은 이동 노드가 즉석에서 만든다. 센서 네트워크(WSN)도 애드혹의 한 갈래로 자주 함께 나온다.

표준 · 교재 정의(OSI · TCP/IP · IEEE · RFC 용어)와 대조했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장이 실제와 다를 수 있다.

03채점 뒤 할 일

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

코드 9문항 중 C 3 · Java 4 · Python 2 다. 17번은 참조형이 Base 면 필드 x 는 Base 것(3)이고 메서드 getX 는 Derived 것(21)이라 21 + 3 + 21 + 7 = 52. 18번은 제네릭 T 가 지워져 Object 라 print(Object) 가 골라져 B0 이다. 둘 다 필드는 참조형 · 메서드는 실제 객체 규칙과 오버로딩은 컴파일 때 정해진다는 규칙이다.

3번 SQL 은 안쪽부터 푼다. 프로젝트별 인원이 2 미만인 project_id 는 20 하나, 그 이름은 Beta, Beta 에 속한 직원은 Charlie 하나라 COUNT 1 이다. 12번은 n1 → n3 → n2 순서로 이어진 리스트에서 앞 두 노드 값만 바뀌어 3 1 2 다.

용어 11문항은 SW공학 4(행위 패턴 분류 · 커버리지 3칸 · UML 관계 3칸 · LRU) · SQL·DB 3(키 4칸 · 개체 무결성 · 서브쿼리) · 네트워크 2(VPN · 애드혹) · 보안 1(스머핑) · URI 구성 요소 매칭이다. 11번 URI 는 기타 묶음이라 분야 장에는 E16 의 표 한 줄뿐이다.

문항마다 가는 장

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

문항분야가는 장무엇을 묻나
1JavaE06String equals 와 향상된 for 출력
2PythonE07앞뒤 교환 뒤 짝수 첨자 합 − 홀수 첨자 합
3SQLE083중 서브쿼리 HAVING COUNT 조인 건수
4OSE16LRU 20개 참조 부재 횟수
5보안E14스머핑 공격
6CE04static 지역 변수 누적 합
7네트워크E11VPN
8디자인패턴E17GoF 행위 패턴 분류
9테스트E18문장 · 분기 · 조건 커버리지 매칭
10DBE10외래키 · 후보키 · 대체키 · 슈퍼키
11E16URI 구성 요소 순서
12CE03연결 리스트 쌍 교환 출력
13DBE10개체 무결성 정의
14PythonE07type 비교 — 문자열 · 실수 · 튜플
15UMLE15UML 연관 · 일반화 · 의존
16JavaE06catch 순서와 finally 합
17JavaE05참조형에 따른 필드 x 와 getX
18JavaE05제네릭 T 는 Object — 오버로딩 선택
19CE02이중 포인터로 배열 제자리 갱신
20네트워크E11애드혹 네트워크

이 회차에서 눈여겨볼 문항

문항왜 갈리나
4번7 0 1 2 0 3 0 4 2 3 0 3 2 1 2 0 1 7 0 1 을 프레임 3개 LRU 로. 부재 12회. 20칸 표를 끝까지 그린다
16번NullPointerException 은 첫 catch 에 잡혀 1, finally 100. 두 번째 catch 는 안 돈다. 101
18번Collection 의 T 는 실행 때 Object 다. print(Object) 가 골라져 B, 값 0 이 붙어 B0
19번update 가 *(*values + i) 로 배열을 제자리에서 바꾼다. (4 + 2) % 5 = 1 이 values[2]

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

04다음

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