2023년 1회 실전 E19

프로그래밍 8 · SQL·DB 4 · 보안 3 · 네트워크 2 · SW공학 2 · AJAX 1. 자바 생성자 체인과 추상 클래스, 비트 분해 빈칸이 갈림길이다

왜 이 장을 하나
2023년 1회는 코드 8문항이 전부 짧다. 대신 static 카운터의 출력 순서, this(500) 생성자 체인, 인자 없는 getName() 처럼 한 줄의 규칙이 답을 가른다. 용어 12문항은 2회분 진단보다 쉬운 편이라 여기서 15문항 아래면 용어 장을 다시 봐야 한다.
다루는 것2023년 1회 20문항 · 복원본
시계150분 · 실제 시험과 같다
끝나면문항마다 가는 장이 표로 나온다

01푸는 법

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

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

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

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

02실전 세트

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

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

2023-1회 · 1번Java

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

class StaticData { public int value = 20; static int count = 0; } public class Main { public static void main(String[] args) { int value = 10; StaticData.count = value; StaticData item = new StaticData(); System.out.println(StaticData.count++); System.out.println(item.count); System.out.println(value); System.out.print(item.value); } }
정답10
11
10
20

— 네 줄: 10 / 11 / 10 / 20. static 인 count 는 클래스에 하나뿐이라 어디서 고치든 같은 칸이고, count++ 는 찍은 뒤에 올린다.

원리static 이 붙은 필드는 객체마다 따로 있지 않고 클래스에 딱 하나 있다. StaticData.count(클래스 이름으로)와 item.count(객체 이름으로)는 표기만 다르고 같은 칸이다. static 이 없는 value 는 객체마다 하나씩 생기며 new 할 때 20 으로 채워진다. main 의 지역 변수 value 는 이름만 같은 다른 칸이다. 후위 count++ 는 "지금 값을 쓰고 나서 1 올린다"라 println 에는 올리기 전 값이 찍힌다.

따라가기

  1. value(지역) = 10, StaticData.count = 10
  2. item 생성 → item.value = 20
  3. println(count++) → 10 을 찍고 count 는 11
  4. println(item.count) → 같은 칸 → 11
  5. println(value) → 지역 변수 → 10, print(item.value) → 20

헷갈리는 자리 — 첫 줄을 11 로 적는 것(++count 였다면 11). item.count 를 "객체 것이니 0"으로 보는 것 — static 은 객체 것이 아니라 클래스 것 하나다. 마지막 줄은 println 이 아니라 print 라 줄바꿈이 없다.

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

2023-1회 · 2번C

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

#include <stdio.h> int main(void) { char word[] = "Art"; char *p = word; printf("%s\n", word); printf("%c\n", *p); printf("%c\n", *word); printf("%s\n", p); for (int i = 0; word[i] != '\0'; i++) { printf("%c", word[i]); } return 0; }
정답Art
A
A
Art
Art

— 다섯 줄: Art / A / A / Art / Art. 배열 이름 word 와 포인터 p 가 같은 자리를 가리키므로 같은 것을 두 번씩 찍는 셈이다.

원리 — C 에서 문자열은 글자를 한 칸에 하나씩 넣은 상자 줄이고 끝에 보이지 않는 끝표('\0')가 붙는다. "Art" 는 A · r · t · 끝표 네 칸이다. 배열 이름 word 는 그 줄의 첫 칸 주소이고, char *p = word 는 그 주소를 종이쪽지 p 에 베껴 적은 것이다. 그래서 word 로 가든 p 로 가든 도착하는 칸은 같다. %s 는 "그 주소부터 끝표 전까지 전부 찍어라", %c 는 "그 칸의 글자 하나만 찍어라"다. 별표 *p 는 "p 가 가리키는 칸의 내용"이라 첫 글자 A 다.

따라가기

  1. printf("%s\n", word) — 첫 칸부터 끝표 전까지 → Art, 줄바꿈.
  2. printf("%c\n", *p) — p 가 가리키는 칸의 글자 → A.
  3. printf("%c\n", *word) — 배열 이름도 첫 칸 주소라 *word 는 첫 글자 → A.
  4. printf("%s\n", p) — p 부터 끝표 전까지 → Art.
  5. for 문 — word[0] 부터 끝표를 만날 때까지 한 글자씩, 줄바꿈 없이 → Art.

헷갈리는 자리 — 줄바꿈 수다. 앞 넷은 \n 이 있고 마지막 for 문은 없으니 답은 다섯 줄이고, 마지막 줄 뒤에 빈 줄이 없다. *word 를 "배열 전체"로 읽어 Art 라고 적는 것이 흔한 오답이다 — 별표는 언제나 칸 하나다.

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

2023-1회 · 3번C

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

#include <stdio.h> int main(void) { char *left = "qwer"; char *right = "qwtety"; for (int i = 0; left[i] != '\0'; i++) { for (int j = 0; right[j] != '\0'; j++) { if (left[i] == right[j]) { printf("%c", left[i]); } } } return 0; }
정답qwe

— qwe. 왼쪽 문자열의 글자를 하나씩 들고 오른쪽 문자열 전체와 견주어, 같은 글자를 만날 때마다 그 글자를 찍는다.

원리 — 이중 for 문은 "바깥 글자 하나마다 안쪽 문자열을 처음부터 끝까지 훑는다"는 뜻이다. 바깥은 left 의 q · w · e · r 네 글자, 안쪽은 right 의 q · w · t · e · t · y 여섯 글자라 비교는 4 × 6 = 24번 일어난다. 그중 두 글자가 같은 순간에만 printf 가 돈다. 그러니 답은 "left 의 글자 중 right 에도 있는 것"을 left 순서대로, 그리고 right 에 그 글자가 여러 번 있으면 그 횟수만큼 찍은 것이다. '\0' 은 문자열 끝표라 거기서 각 반복이 멈춘다.

따라가기

  1. i = 0, q — right 에 q 가 1개 → q
  2. i = 1, w — right 에 w 가 1개 → w
  3. i = 2, e — right 에 e 가 1개(q w t e t y) → e
  4. i = 3, r — right 에 r 없음 → 아무것도 안 찍힘
  5. 합쳐서 qwe

헷갈리는 자리 — right 에 t 가 두 개라 t 를 두 번 적는 것. t 는 left 에 없으므로 바깥 반복이 t 를 들고 오는 일이 없다. 찍히는 글자는 언제나 left 쪽 순서이고, 중복 횟수는 right 쪽 개수를 따른다는 두 규칙을 나눠 기억한다.

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

2023-1회 · 4번웹기술

JavaScript와 XML을 이용해 웹 페이지 전체를 새로고침하지 않고 서버와 비동기적으로 데이터를 교환하는 기술은? (Asynchronous JavaScript and XML)

정답AJAX

— AJAX(Asynchronous JavaScript and XML). 페이지를 통째로 다시 받지 않고 자바스크립트가 뒤에서 서버와 데이터만 주고받아 화면 일부를 바꾸는 기법이다.

원리 — 옛 웹은 버튼 하나를 눌러도 브라우저가 서버에 새 페이지를 요청해 화면 전체를 다시 그렸다. AJAX 는 자바스크립트의 XMLHttpRequest(지금은 fetch) 객체로 서버에 "데이터만" 요청하고, 응답이 오면 DOM 의 필요한 부분만 바꾼다. "비동기"라는 것은 요청을 보내 놓고 응답을 기다리는 동안 사용자는 계속 화면을 쓸 수 있다는 뜻이다. 이름의 XML 은 초기에 데이터 형식으로 XML 을 썼기 때문이고 지금은 대부분 JSON 을 쓴다 — 이름만 남았다. 검색창 자동완성, 무한 스크롤이 전부 AJAX 다.

따라가기

  1. "JavaScript 와 XML 을 이용" → 이름의 J · X
  2. "전체를 새로고침하지 않고 비동기적으로 교환" → Asynchronous
  3. 합치면 AJAX

헷갈리는 자리 — 영문 풀이를 쓸 때 Asynchronous 철자를 틀리는 것. 그리고 JSON 을 답으로 적는 실수 — JSON 은 데이터 "형식"이고 AJAX 는 데이터를 "주고받는 기법"이다. 같은 문제가 2025-2-08 에 다시 나왔다.

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

2023-1회 · 5번네트워크

패킷교환 방식 2가지를 쓰시오. (하나는 연결형으로 경로를 미리 설정, 하나는 비연결형으로 각 패킷이 독립적으로 전송)

정답가상 회선 방식, 데이터그램 방식

— 가상 회선 방식, 데이터그램 방식. 패킷을 보내기 전에 길을 정하느냐(연결형), 패킷마다 각자 길을 찾느냐(비연결형)의 차이다.

원리 — 패킷 교환은 데이터를 작은 덩어리(패킷)로 잘라 보내는 방식이고 그 안에서 둘로 갈린다. 가상 회선은 통화처럼 먼저 길(논리적 경로)을 정한 뒤 모든 패킷이 같은 길로 순서대로 간다 — 연결형이라 도착 순서가 보장되고 설정 시간이 든다. 데이터그램은 우편처럼 패킷마다 주소를 붙여 각자 가장 좋은 길로 보낸다 — 비연결형이라 순서가 뒤바뀔 수 있고 설정이 없어 빠르다. 인터넷의 IP 가 데이터그램 방식이다.

따라가기

  1. 경로를 미리 설정 · 연결형 → 가상 회선
  2. 패킷마다 독립 전송 · 비연결형 → 데이터그램

헷갈리는 자리 — 회선 교환(전화선처럼 물리적으로 회선을 독점)과 가상 회선을 섞는 것. 가상 회선은 패킷 교환 안의 한 갈래이고 회선을 독점하지 않는다. 2024년 2회 9번에 같은 문제가 번호를 붙여 다시 나왔다.

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

2023-1회 · 6번보안

시스코의 L2F와 마이크로소프트의 PPTP를 조합한 VPN 터널링 프로토콜로, 자체 암호화 기능이 없어 IPsec과 함께 사용되는 것은?

정답L2TP

— L2TP. 시스코의 L2F 와 마이크로소프트의 PPTP 를 합친 2계층 터널링 프로토콜이고, 암호화가 없어 IPSec 과 짝지어 쓴다.

원리 — VPN 은 공중망 위에 터널을 뚫어 사설망처럼 쓰는 것이고, 터널을 뚫는 방법(터널링 프로토콜)이 여럿이다. PPTP 는 마이크로소프트 것, L2F 는 시스코 것이었는데 둘을 표준으로 합친 것이 L2TP(Layer 2 Tunneling Protocol)다. 이름의 2 가 데이터링크(2계층)에서 터널을 만든다는 뜻이다. L2TP 는 캡슐에 싸기만 하고 내용을 암호화하지 않으므로, 실제로는 3계층 암호화 프로토콜인 IPSec 과 함께(L2TP/IPSec) 쓴다. "L2F + PPTP", "자체 암호화 없음 + IPSec" 이 표지다.

따라가기

  1. L2F + PPTP 조합 → L2TP
  2. 자체 암호화 없음 → IPSec 과 함께 → 확인

헷갈리는 자리 — PPTP 나 IPSec 을 적는 것. PPTP 는 합치기 전 재료이고 IPSec 은 함께 쓰는 짝이다. "2계층"은 L2TP · PPTP, "3계층"은 IPSec 이라는 층 구분도 함께 나온다.

표준 · 교재 정의(암호 · 인증 · 접근 통제 용어)와 대조했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장이 실제와 다를 수 있다.

2023-1회 · 7번보안

22번 포트를 사용하며, 암호화된 원격 로그인·원격 명령 실행·파일 전송을 제공하는 보안 프로토콜은?

정답SSH

— SSH. 22번 포트에서 원격 로그인 · 명령 실행 · 파일 전송을 전부 암호화해 주는 프로토콜이다.

원리 — Telnet(23)은 원격 접속 내용이 평문이라 중간에서 비밀번호까지 보인다. SSH(Secure Shell)는 같은 일을 암호화된 통로로 하며, 그 통로로 원격 명령 실행, 파일 복사(SCP · SFTP), 포트 포워딩까지 한다. 공개키 방식으로 서버를 확인하고 세션 키를 만들어 대칭 암호로 통신한다. 기본 포트가 22 라 "22 · 암호화 · 원격 로그인" 셋이 SSH 의 표지다.

따라가기

  1. 22번 포트 + 암호화 원격 로그인 → SSH

헷갈리는 자리 — SSL/TLS(웹 · 443)나 Telnet(23)과 섞는 것. "원격 로그인"이면 SSH 또는 Telnet 이고, "암호화"가 붙으면 SSH 다. 2025년 2회 3번은 거꾸로 SSH 의 포트 번호를 물었다.

표준 · 교재 정의(암호 · 인증 · 접근 통제 용어)와 대조했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장이 실제와 다를 수 있다.

2023-1회 · 8번보안

악성코드 3종을 구분하시오. ㄱ) 자가 복제하며 네트워크로 스스로 전파됨 ㄴ) 정상 프로그램으로 위장해 숨어들며 자가 복제하지 않음 ㄷ) 자가 복제는 가능하나 스스로 전파되지 못하고 숙주 파일이 필요함

정답ㄱ 웜(Worm) / ㄴ 트로이 목마 / ㄷ 바이러스

— ㄱ 웜 ㄴ 트로이 목마 ㄷ 바이러스. "스스로 퍼지나 · 복제하나 · 숙주가 필요하나" 세 물음으로 갈린다.

원리 — 악성코드는 퍼지는 방식으로 구분한다. 바이러스는 다른 파일(숙주)에 붙어 그 파일이 실행될 때 함께 실행되고 복제되며, 스스로 네트워크를 타지는 못한다 — 사람이 파일을 옮겨야 퍼진다. 웜은 숙주 없이 혼자 실행되고 네트워크의 취약점을 타고 스스로 다른 컴퓨터로 퍼진다 — 가장 빠르게 번진다. 트로이 목마는 유용한 프로그램인 척 사용자가 직접 설치하게 만들고, 복제하지 않으며 뒷문 · 정보 유출 같은 몰래 하는 일을 한다. "복제 + 자가 전파 = 웜, 복제 + 숙주 = 바이러스, 복제 없음 + 위장 = 트로이"다.

따라가기

  1. 자가 복제 + 네트워크로 스스로 전파 →
  2. 정상 프로그램 위장 + 복제 안 함 → 트로이 목마
  3. 복제는 하나 숙주 파일 필요 → 바이러스

헷갈리는 자리 — 웜과 바이러스를 바꾸는 것. "스스로 전파"면 웜, "숙주"면 바이러스다. 트로이 목마는 "복제하지 않는다"가 결정적이다.

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

2023-1회 · 9번C

다음 C 프로그램에서 ①과 ②에 들어갈 연산자와 값을 순서대로 쓰시오.

#include <stdio.h> int main(void) { int input = 101110; int place = 1; int sum = 0; while (input != 0) { sum += (input ① 10) * place; place *= ②; input /= 10; } printf("%d", sum); return 0; }
정답① %, ② 2

— ① % ② 2. 2진수로 적힌 수 101110 을 10진수 46 으로 바꾸는 코드다.

원리 — 2진수 101110 은 자리마다 값이 다르다. 오른쪽 끝 자리는 1, 그다음은 2, 4, 8, 16, 32 다. 그러니 10진수로 바꾸려면 "각 자리의 숫자 × 그 자리의 값"을 전부 더하면 된다. 코드는 input 을 10 으로 나눈 나머지(input % 10)로 맨 오른쪽 자리를 떼어 내고, input /= 10 으로 그 자리를 버린다. 자리 값 place 는 1 에서 시작해 자리를 옮길 때마다 2배(place *= 2)가 된다. 그래서 ① 은 한 자리를 떼는 나머지 연산, ② 는 자리 값을 키우는 2 다.

따라가기 — input = 101110, place = 1, sum = 0.

  1. 0 × 1 = 0, place 2, input 10111
  2. 1 × 2 = 2, place 4, input 1011
  3. 1 × 4 = 4, place 8, input 101
  4. 1 × 8 = 8, place 16, input 10
  5. 0 × 16 = 0, place 32, input 1
  6. 1 × 32 = 32, input 0 → 끝. sum = 0 + 2 + 4 + 8 + 0 + 32 = 46

헷갈리는 자리 — ② 에 10 을 적는 것. 10 을 곱하면 101110 을 그대로 다시 만드는 셈이라 아무 변환도 안 된다. 2진 → 10진은 자리 값이 2배씩이라 2 다. ① 에 / 를 적으면 자리를 떼는 게 아니라 버리는 것이라 값이 커진다. 답은 기호 하나와 숫자 하나다.

빈칸에 답을 넣어 컴파일 · 실행해 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2023-1회 · 10번네트워크

IP 패킷 전송 중 오류 발생 시 오류 정보를 보고하고 진단하는 프로토콜로, ping 명령이 사용하는 것은?

정답ICMP

— ICMP. IP 계층에서 오류와 상태를 알리는 심부름 프로토콜이고, ping 이 그것으로 응답을 묻는다.

원리 — IP 는 패킷을 보내기만 하고 도착했는지 · 왜 못 갔는지를 알려 주지 않는다. 그 빈자리를 채우는 것이 ICMP(Internet Control Message Protocol)다 — "목적지에 닿을 수 없음", "시간 초과", "에코 요청 · 응답" 같은 메시지를 IP 패킷에 실어 보낸다. ping 은 에코 요청을 보내고 에코 응답이 오는지로 상대가 살아 있는지 확인하고, traceroute 는 시간 초과 메시지로 경로를 알아낸다. 네트워크 계층(3계층)에 속한다.

따라가기

  1. "IP 전송 중 오류 보고 · 진단" → 제어 메시지
  2. "ping 이 사용" → ICMP

헷갈리는 자리 — IGMP(멀티캐스트 그룹 관리)나 SNMP(장비 관리)와 섞는 것. "오류 · ping" 은 ICMP 다. ARP(IP → MAC)와도 다르다 — ARP 는 주소를 찾고 ICMP 는 상태를 알린다.

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

2023-1회 · 11번디자인패턴

특정 객체에 대한 접근을 제어하기 위해 그 객체의 대리자(surrogate)를 두어 원래 객체의 작업을 대신 처리하게 하는 GoF 디자인 패턴은?

정답Proxy (프록시)

— 프록시(Proxy). 진짜 객체 앞에 대리인을 세워 접근을 거르거나 미루거나 기록하는 구조 패턴이다.

원리 — 클라이언트는 진짜 객체와 같은 인터페이스를 가진 대리 객체를 부른다. 대리 객체는 요청을 받아 "권한이 있나 확인(보호 프록시)", "진짜 객체를 아직 안 만들었으면 지금 만들기(가상 프록시 · 지연 로딩)", "원격 서버에 대신 전달(원격 프록시)", "결과를 캐시" 같은 일을 한 뒤 필요할 때만 진짜에게 넘긴다. 클라이언트는 대리인인지 진짜인지 모른다 — 그래서 접근 제어를 진짜 객체 코드를 건드리지 않고 끼워 넣을 수 있다. JPA 의 지연 로딩 엔티티, 스프링 AOP 프록시가 이 패턴이다.

따라가기

  1. "특정 객체에 대한 접근을 제어" → 앞에서 거른다
  2. "대리자(surrogate)를 두어 대신 처리" → 같은 인터페이스의 대리 객체
  3. 프록시

헷갈리는 자리 — 데코레이터(Decorator)와 섞는 것. 둘 다 같은 인터페이스로 감싸지만, 데코레이터는 "기능을 덧붙이는" 것이 목적이고 프록시는 "접근을 제어하는" 것이 목적이다. 어댑터는 인터페이스를 "바꾸는" 것이라 또 다르다. 같은 문제가 2025-2-07 에 다시 나왔다.

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

2023-1회 · 12번DB

관계형 DB 용어: ① 릴레이션의 행(Row)에 해당하는 것 ② 실제 저장된 값(튜플)들의 집합 ③ 튜플의 개수

정답① 튜플 ② 릴레이션 인스턴스 ③ 카디널리티

— ① 튜플 ② 릴레이션 인스턴스 ③ 카디널리티. 표의 행 · 행들의 집합 · 행의 개수를 부르는 관계형 모델의 이름이다.

원리 — 관계형 데이터베이스는 표를 다른 이름으로 부른다. 표 = 릴레이션, 세로 한 줄(열) = 속성(attribute), 가로 한 줄(행) = 튜플이다. 표의 머리(열 이름들의 틀)는 릴레이션 스키마, 그 틀에 실제로 들어 있는 행들의 집합은 릴레이션 인스턴스다. 개수에도 이름이 있다 — 행의 개수는 카디널리티, 열의 개수는 차수(degree)다. "틀"과 "내용물"을 나누는 것이 이 용어 묶음의 뼈대다.

따라가기

  1. 행(Row)에 해당 → 튜플
  2. 실제 저장된 값(튜플)들의 집합 → 릴레이션 인스턴스
  3. 튜플의 개수 → 카디널리티

헷갈리는 자리 — 카디널리티(행 수)와 차수(열 수)를 바꿔 적는 것. "카드 한 장 = 한 행"으로 외우면 카디널리티가 행 수다. ② 에 스키마를 적는 것 — 스키마는 값이 아니라 틀이다. 2025년 3회 18번에 같은 문제가 그대로 다시 나왔다.

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

2023-1회 · 13번SQL

학생 테이블에서 이름이 '민수'인 튜플을 삭제하는 SQL문을 작성하시오. (구문 끝에 세미콜론 포함)

정답DELETE FROM 학생 WHERE 이름 = '민수';

DELETE FROM 학생 WHERE 이름 = '민수';

원리 — 행을 지우는 명령은 DELETE FROM 테이블 WHERE 조건 이다. DELETE 뒤에 열 이름을 적지 않는다 — 행 전체가 지워지므로 "무엇을"이 아니라 "어느 테이블에서 · 어느 행을"만 말한다. WHERE 를 빼면 테이블의 모든 행이 지워지므로 조건이 답의 핵심이다. 문자열 값은 작은따옴표로 감싼다.

따라가기

  1. 어느 테이블 → DELETE FROM 학생
  2. 어느 행 → WHERE 이름 = '민수'
  3. 끝에 세미콜론(문제가 포함하라고 했다)

헷갈리는 자리DELETE * FROM 학생 처럼 * 를 넣는 것(SELECT 와 섞임 — 오류). DROP TABLE 은 테이블 자체를 없애는 것이라 다른 명령이다. 따옴표를 큰따옴표로 쓰거나 빼는 것도 감점 자리다. DELETE 는 DML 이라 COMMIT 전에는 ROLLBACK 으로 되돌릴 수 있지만, 같은 결과를 내는 TRUNCATE 나 DROP 은 되돌릴 수 없다 — 이 차이도 같은 묶음에서 자주 나온다.

답의 SQL 을 sqlite 3.49 에서 실행해 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 열 이름이 실제와 다를 수 있다.

2023-1회 · 14번C

다음 C 프로그램에서 ①과 ②에 들어갈 변수명을 순서대로 쓰시오.

#include <stdio.h> void swap(int *arr, int idx1, int idx2) { int temp = arr[idx1]; arr[idx1] = arr[idx2]; arr[①] = temp; } void bubbleSort(int *arr, int length) { for (int i = 0; i < length - 1; i++) { for (int j = 0; j < length - 1 - i; j++) { if (arr[j] > arr[j + 1]) { swap(arr, j, j + 1); } } } } int main(void) { int values[] = { 85, 75, 50, 100, 95 }; int size = 5; bubbleSort(values, ②); return 0; }
정답① idx2, ② size

— ① idx2 ② size. ① 은 두 값을 맞바꾸는 마지막 단계에서 비어 있는 자리이고, ② 는 정렬 함수에 넘길 배열의 길이다.

원리 — 두 컵의 물을 맞바꾸려면 빈 컵이 하나 더 필요하다. temp = arr[idx1](첫째 컵을 빈 컵에), arr[idx1] = arr[idx2](둘째 컵을 첫째 컵에), 마지막으로 빈 컵의 물을 둘째 컵에 부어야 하니 arr[idx2] = temp 다. 이 세 줄은 순서가 정해져 있어 한 줄만 보여도 나머지가 정해진다. 버블 정렬은 이웃한 두 칸을 비교해 앞이 크면 맞바꾸는 일을 배열 길이만큼 반복하는 것이라, 함수는 배열과 그 길이 두 가지를 알아야 한다.

따라가기

  1. swap 의 첫 두 줄에서 idx1 자리는 이미 idx2 의 값으로 덮였다. 아직 안 채워진 곳은 idx2 자리뿐 → ① idx2.
  2. bubbleSort(int *arr, int length) — 둘째 인자는 길이. main 에 길이를 담은 변수는 size = 5 하나 → ② size.
  3. 넣고 돌리면 85 75 50 100 95 → 50 75 85 95 100 으로 정렬된다(출력문은 없어 화면엔 안 찍힌다).

헷갈리는 자리 — ② 에 숫자 5 를 적는 것. 문제가 "변수명"을 물었으므로 5 는 틀린다. ① 에 idx1 을 적으면 첫째 컵에 두 번 붓는 꼴이라 둘째 컵이 그대로 남는다 — 맞바꾸기가 안 된다.

빈칸에 답을 넣어 gcc(도커 alpine)로 컴파일 · 실행해 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2023-1회 · 15번Python

다음 Python 프로그램의 실행 결과에 남는 집합 원소를 쓰시오. 원소의 출력 순서는 고려하지 않는다.

countries = {'한국', '중국', '일본'} countries.add('베트남') countries.add('중국') countries.remove('일본') countries.update({'홍콩', '한국', '태국'}) print(countries)
정답한국, 중국, 베트남, 홍콩, 태국(순서 무관)

— 한국, 중국, 베트남, 홍콩, 태국 (순서 무관). 집합은 같은 것을 두 번 넣어도 하나만 남기고, remove 로 뺀 일본은 돌아오지 않는다.

원리 — 집합(set)은 "무엇이 있나"만 기억하는 자루다. 같은 원소를 다시 넣어도(add('중국')) 자루 안은 그대로이고, 순서도 없어서 출력 순서는 그때그때 다르다 — 그래서 문제가 "순서는 고려하지 않는다"고 한 것이다. add 는 하나 넣기, remove 는 하나 빼기(없으면 오류), update 는 여러 개를 한꺼번에 넣기다. update 안에 이미 있는 것이 섞여 있어도 새 것만 늘어난다.

따라가기

  1. 처음 {한국, 중국, 일본}
  2. add 베트남 → {한국, 중국, 일본, 베트남}
  3. add 중국 → 이미 있어 변화 없음
  4. remove 일본 → {한국, 중국, 베트남}
  5. update {홍콩, 한국, 태국} → 한국은 있으니 홍콩 · 태국만 추가 → {한국, 중국, 베트남, 홍콩, 태국}

헷갈리는 자리 — 중국이나 한국을 두 번 적는 것(집합에 중복은 없다). 일본을 남기는 것(remove 뒤 update 에 일본은 없다). 답안에는 원소 다섯을 쉼표로 적으면 되고 중괄호 · 순서는 채점 대상이 아니다.

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

2023-1회 · 16번SQL

성적 테이블에서 과목이름별 최소점수·최대점수를 구하되, 평균 점수가 90 이상인 과목만 조회하는 SQL을 작성하시오. (컬럼 별칭: 최소점수, 최대점수)

정답SELECT 과목이름, MIN(점수) AS 최소점수, MAX(점수) AS 최대점수 FROM 성적 GROUP BY 과목이름 HAVING AVG(점수) >= 90;

SELECT 과목이름, MIN(점수) AS 최소점수, MAX(점수) AS 최대점수 FROM 성적 GROUP BY 과목이름 HAVING AVG(점수) >= 90;

원리 — "과목이름별"이라는 말이 나오면 GROUP BY 과목이름 이다. 같은 과목의 행을 한 묶음으로 접고, 묶음마다 MIN · MAX · AVG 같은 집계를 낸다. 묶음에 조건을 거는 자리는 WHERE 가 아니라 HAVING 이다 — WHERE 는 묶기 전 행 하나하나에, HAVING 은 묶은 뒤 집계값에 건다. "평균이 90 이상인 과목만"은 묶은 뒤의 조건이라 HAVING AVG(점수) >= 90 이다. AS 는 결과 열의 이름표(별칭)를 붙인다.

따라가기

  1. 무엇을 뽑나 → 과목이름, 최소, 최대 → SELECT 과목이름, MIN(점수) AS 최소점수, MAX(점수) AS 최대점수
  2. 어디서 → FROM 성적
  3. 무엇별로 → GROUP BY 과목이름
  4. 묶음 조건 → HAVING AVG(점수) >= 90

헷갈리는 자리 — HAVING 자리에 WHERE AVG(점수) >= 90 을 쓰는 것. WHERE 안에는 집계 함수를 못 쓴다(오류). 별칭 이름을 문제가 준 그대로(최소점수 · 최대점수) 적어야 하고, "이상"은 >= 다(> 는 초과). GROUP BY 에 없는 열을 SELECT 에 넣으면 안 된다는 규칙도 같이 기억한다.

답의 SQL 을 sqlite 3.49 에서 실행해 결과를 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 열 이름이 실제와 다를 수 있다.

2023-1회 · 17번Java

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

abstract class Vehicle { String name; abstract public String getName(String value); public String getName() { return "Vehicle name: " + name; } } class Car extends Vehicle { public Car(String value) { name = value; } public String getName(String value) { return "Car name: " + value; } } public class Main { public static void main(String[] args) { Vehicle vehicle = new Car("Spark"); System.out.println(vehicle.getName()); } }
정답Vehicle name: Spark

Vehicle name: Spark. 인자 없는 getName() 은 부모에만 있으므로 부모 것이 돌고, name 에는 생성자가 넣은 Spark 가 들어 있다.

원리 — 이름이 같은 메서드가 둘 있어도 매개변수의 꼴이 다르면 서로 다른 메서드다(오버로딩). Car 의 getName(String value) 는 인자를 하나 받는 메서드이고, Vehicle 의 getName() 은 인자가 없는 메서드라 둘은 남남이다. 자식이 부모 메서드를 갈아끼우는 것(오버라이딩)은 이름과 매개변수 꼴이 완전히 같을 때만 일어난다. vehicle.getName() 은 인자가 없으니 갈아끼워진 적 없는 부모의 것이 그대로 돈다. 추상 클래스는 직접 만들 수 없지만 자식 객체를 부모 이름으로 담는 것은 된다.

따라가기

  1. new Car("Spark") → Car 생성자에서 부모 필드 name = "Spark"
  2. vehicle.getName() — 인자 없음 → Vehicle.getName()
  3. "Vehicle name: " + name → Vehicle name: Spark

헷갈리는 자리 — Car 에 getName 이 있으니 "Car name: Spark" 라고 적는 것. 그것은 인자를 받는 다른 메서드라 이 호출로는 불리지 않는다. 오버로딩(같은 이름 · 다른 매개변수)과 오버라이딩(같은 이름 · 같은 매개변수)을 나누는 문제이고, 2024년 1회 10번의 getT() 도 같은 함정이다.

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

2023-1회 · 18번DB

스키마 3계층: ① 사용자 관점의 뷰 ② 조직 전체의 논리적 구조 ③ 물리적 저장 구조

정답① 외부 스키마 ② 개념 스키마 ③ 내부 스키마

— ① 외부 스키마 ② 개념 스키마 ③ 내부 스키마. 데이터베이스를 보는 세 눈높이의 이름이다.

원리 — 같은 데이터베이스를 누가 보느냐에 따라 세 층으로 나눈다. 외부 스키마는 사용자 · 프로그램마다 보이는 부분(뷰) — 여러 개일 수 있다. 개념 스키마는 조직 전체의 논리적 구조 하나 — 어떤 표가 있고 어떻게 이어지는지. 내부 스키마는 실제 저장 방식 — 파일 · 인덱스 · 저장 순서. 위에서 아래로 "보이는 것 → 전체 설계 → 저장"이라고 읽는다. 층을 나눈 이유는 한 층을 바꿔도 다른 층이 안 흔들리게(데이터 독립성) 하려는 것이다.

따라가기

  1. 사용자 관점의 뷰 → 외부
  2. 조직 전체의 논리적 구조 → 개념
  3. 물리적 저장 구조 → 내부

헷갈리는 자리 — 개념과 내부를 바꾸는 것. "개념"은 사람이 그리는 설계도, "내부"는 디스크다. 외부 스키마는 서브 스키마라고도 부른다. 개념 스키마가 여럿이라고 적으면 틀린다 — 하나다.

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

2023-1회 · 19번테스트

다음 제어 흐름에서 분기 커버리지를 만족하도록 두 개의 실행 경로를 쓰시오.

제어 흐름도(그림을 표로 옮김)

노드내용다음
1POINTER = FALSE2
2X > K ?YES → 3, NO → 4
3POINTER = TRUE4
4X = X + 25
5CALL SUB(X, POINTER, RESULT)6
6RESULT > 1 ?YES → 1, NO → 7
7PRINT RESULT
정답1234561, 124567 (또는 1234567, 124561)

— 1234561 과 124567 (또는 1234567 과 124561). 분기가 두 곳(2번 · 6번)이라 각각 참 · 거짓을 한 번씩 밟는 경로 두 개면 된다.

원리 — 분기(결정) 커버리지는 "모든 분기의 참 · 거짓이 각각 한 번 이상 실행됐는가"다. 이 흐름도의 분기는 2번(X > K ?)과 6번(RESULT > 1 ?) 둘이다. 2번 YES 면 3 을 거쳐 4 로, NO 면 바로 4 로 간다. 6번 YES 면 1 로 되돌아가고, NO 면 7 로 가 끝난다. 경로 하나가 각 분기에서 한쪽을 밟으므로, 두 경로로 네 가지(2Y · 2N · 6Y · 6N)를 모두 덮으려면 첫 경로가 2Y 와 6 의 한쪽, 둘째 경로가 2N 과 6 의 반대쪽을 밟으면 된다. 그래서 짝짓기 방법이 두 가지 나온다.

따라가기

  1. 2Y 경로: 1 → 2 → 3 → 4 → 5 → 6, 2N 경로: 1 → 2 → 4 → 5 → 6
  2. 6Y 면 → 1 로 돌아감, 6N 이면 → 7 로 끝
  3. 짝 A: 2Y+6Y = 1234561, 2N+6N = 124567
  4. 짝 B: 2Y+6N = 1234567, 2N+6Y = 124561
  5. 어느 짝이든 네 갈래를 다 밟는다

헷갈리는 자리 — 1234567 과 1234561 을 쓰는 것. 둘 다 2 에서 YES 만 밟아 2N 이 빠진다 — 분기 커버리지 미달이다. 6번에서 YES 로 1 에 돌아가는 경로의 끝을 "1" 로 적는 것이 어색해 보여도, 흐름도가 1 로 되돌아가니 그대로 적는다. 같은 문제가 2025-2-11 에 다시 나왔다.

제어 흐름 표의 분기(2 · 6번 노드)를 참 · 거짓으로 나눠 경로를 손으로 세었다. 복원 출처 grandlife.co.kr(2026-09-14) — 그림은 표로 옮겼다.

2023-1회 · 20번Java

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

class Parent { int x = 100; Parent() { this(500); } Parent(int x) { this.x = x; } int getX() { return x; } } class Child extends Parent { int x = 4000; Child() { this(5000); } Child(int x) { this.x = x; } } public class Main { public static void main(String[] args) { Child item = new Child(); System.out.println(item.getX()); } }
정답500

— 500. Child 를 만들 때 부모 생성자 사슬이 Parent 의 x 를 500 으로 만들고, getX() 는 부모 메서드라 부모의 x 를 돌려준다.

원리this(500) 은 같은 클래스의 다른 생성자를 부르는 것이고, 자식 생성자는 첫 줄에 super() 를 안 적어도 부모의 인자 없는 생성자가 몰래 먼저 불린다. 그래서 new Child() 는 Child() → Child(5000) → (암묵) Parent() → Parent(500) 순으로 이어진다. 부모와 자식이 같은 이름 x 를 각각 가지면 두 칸이 따로 있다(필드는 갈아끼워지지 않는다). getX() 는 Parent 에만 있으므로 그 안의 x 는 Parent 의 x 다.

따라가기

  1. Child() → this(5000) → Child(int x) 로 들어가기 전에 부모 생성자 Parent() 호출
  2. Parent() → this(500) → Parent(int x): Parent.x = 500
  3. 돌아와서 Child(int x): Child.x = 5000
  4. item.getX() → Parent 의 getX → Parent.x → 500

헷갈리는 자리 — 5000(자식 x)이나 4000(자식 초기값)을 적는 것. getX 는 자식 칸을 볼 수 없다. 100 을 적는 것은 this(500) 을 못 본 것이다. 필드는 참조형 · 소속 클래스에 따라, 메서드는 실제 객체에 따라 정해진다는 규칙을 이 문제로 외운다.

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

03채점 뒤 할 일

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

코드 8문항 중 C 4 · Java 3 · Python 1 이다. 빈칸 채우기가 둘(9번 자릿수 분해, 14번 버블 정렬)이라 코드를 끝까지 읽기 전에 빈칸부터 채우려는 습관을 경계한다. 빈칸은 그 줄이 무엇을 하려는지 앞뒤 줄이 말해 준다.

Java 세 문항이 전부 클래스 규칙이다. 1번은 static 필드가 인스턴스 사이에 공유되고 count++ 가 값을 먼저 찍는다는 것, 17번은 인자 없는 getName() 이 부모 것 하나뿐이라 오버라이딩이 아니라는 것, 20번은 this(500) 이 같은 클래스의 다른 생성자를 부른다는 것이다. 셋 다 E05 · E06 의 03절 첫 그림이다.

용어 12문항은 보안 3(L2TP · SSH · 악성코드 3종) · 네트워크 2(패킷 교환 · ICMP) · DB 4(용어 3칸 · DELETE · GROUP BY HAVING · 스키마) · 디자인 패턴(Proxy) · 커버리지 경로 · AJAX 다. 19번 분기 커버리지는 경로 두 개를 적는 문제라 부분 점수가 있다.

문항마다 가는 장

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

문항분야가는 장무엇을 묻나
1JavaE06static 변수와 인스턴스 변수의 공유
2CE02문자 배열과 포인터의 같은 자리 출력
3CE03두 문자열의 공통 문자 이중 반복
4웹기술E16AJAX 의 정의
5네트워크E11패킷 교환 2방식 이름
6보안E13L2TP 터널링 프로토콜
7보안E1322번 포트 SSH
8보안E14웜 · 트로이 목마 · 바이러스 구분
9CE04자릿수 분해 빈칸 — % 와 place *= 2
10네트워크E11ping 이 쓰는 ICMP
11디자인패턴E17대리자 Proxy 패턴
12DBE10튜플 · 릴레이션 인스턴스 · 카디널리티
13SQLE09DELETE 문 작성
14CE02swap · bubbleSort 빈칸 변수명
15PythonE07집합 add · remove · update 결과
16SQLE08GROUP BY + HAVING AVG 작성
17JavaE05추상 클래스 오버로딩 — 인자 없는 getName
18DBE10스키마 3계층 이름
19테스트E18분기 커버리지 경로 2개
20JavaE05this(…) 생성자 체인과 필드 가림

이 회차에서 눈여겨볼 문항

문항왜 갈리나
1번count 는 value 로 10 이 된 뒤 count++ 가 10 을 찍고 11 이 된다. 그다음 item.count 11, value 10, item.value 20 — 네 줄의 순서가 답이다
9번101110 을 10으로 나눈 나머지를 2의 자리값으로 모으면 2진수 → 10진수 변환이다. 첫째 빈칸 %, 둘째 빈칸 2
17번vehicle.getName() 은 인자가 없어 부모 getName() 하나뿐이다. Car 의 getName(String) 은 오버로딩이라 안 불린다
20번Child() → this(5000) → super() 암묵 호출 → Parent() → this(500). getX 는 Parent 의 x 를 돌려주므로 500

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

04다음

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