자격증 요약/정보처리기사

[25년 2회] 정보처리기사 실기 문제 풀이

문성 2026. 2. 24. 08:00


1. 다음은 파일 구조와 관련된 설명이다.설명을 읽고 괄호 안에 들어갈 가장 알맞은 용어를 작성하시오.

- 데이터베이스의 물리 설계 시,레코드에 접근하는 방법은 순차 접근 방법, [     ]방법,해싱 방법 등이 있다.
- 이 중[     ]방법은 레코드의 키 값과 포인터를 쌍으로 묶어 저장하며 검색 시 키 값을 기준으로 빠르게 탐색할 수 있도록 설계되어 있다.
- 이 방식은 검색 속도가 빠르며<키 값,포인터>쌍으로 구성된 자료 구조를 사용하여 해당 키가 가리키는 주소를 통해 원하는 레코드를 직접 찾을 수 있다.

답: 인덱스 or 색인
                               

인덱스(Index): 검색 속도를 높이기 위해 <키 값, 포인터> 쌍으로 구성한 보조 자료 구조. (책의 색인)
해싱(Hashing): 해시 함수를 이용해 키 값을 주소로 직접 변환하여 접근하는 방식입니다. 검색 속도가 가장 빠름.
JSON: 데이터 객체를 속성(Key): 값(Value) 쌍으로 표현하는 텍스트 기반의 데이터 포맷입니다. 주로 네트워크에서 데이터를 주고받을 때 사용.
※ JSON vs 인덱스: 둘 다 키-값 쌍이지만, JSON은 데이터 교환용 '형식'이고, 인덱스는 DB 검색용 '지표'입니다.

 

 

2. 다음은 데이터베이스 릴레이션의 구성 요소 중 하나에 대한 설명이다.설명을 읽고 보기에서 알맞은 기호를 골라 작성하시오.

- 릴레이션(Relation)에서 열(Column)을 의미하며 데이터 항목의 속성(Attribute)또는 특성을 나타낸다.
- 각 열은 고유한 이름을 가지며 특정 도메인(Domain)에서 정의된 값을 갖는다.
- 예를 들어"학생"릴레이션에서 학번,이름,전공 등은 각각 하나의 열이며 이 열들은 학생의 고유한 속성을 나타낸다.
- 이 개념은 파일 구조에서의 필드(Field)에 해당하며 릴레이션에서 행(Row, Tuple)의 구성 요소가 된다.
[보기] ㄱ. Cardinality ㄴ. Domain ㄷ. Attribute ㅁ. Degree ㅂ. Schema ㅅ. Tuple
답: ㄷ. Attribute
                               

스키마 (Schema)
 - 데이터베이스의 구조(Structure)와 제약 조건에 관한 전반적인 명세를 정의한 '틀'
 - 속성, 개체, 관계 등을 정의. → '구조적 정의', '데이터베이스의 뼈대'

속성 (Attribute)
 - 데이터베이스를 구성하는 가장 작은 논리적 단위이며, 파일 구조상의 '항목(Field)' 또는 '열(Column)'
 - 개체의 특성을 기술합니다.

튜플 (Tuple)
 - 테이블 내의 한 행(Row)을 의미하며, 하나의 레코드(Record)와 같음
 - 속성들의 모임으로 구성됩니다.

차수 (Degree)
 - 하나의 릴레이션(테이블) 안에 있는 속성(열)의 전체 개수
 - '세로 줄이 몇 개인가'

카디널리티 (Cardinality)
 - 하나의 릴레이션(테이블) 안에 있는 튜플(행)의 전체 개수
 - '가로 줄이 몇 개인가'

인스턴스 (Instance)
 - 특정 시점에 데이터베이스에 들어있는 실제 데이터 값들의 집합
 - 스키마는 잘 변하지 않지만, 인스턴스는 데이터의 삽입/삭제에 따라 수시로 변함

도메인 (Domain)
 - 하나의 속성(Attribute)이 취할 수 있는 동일한 유형의 원자값(Atomic Value)들의 집합
 - 데이터의 무결성을 유지하기 위해 특정 속성에 들어갈 값의 범위를 제한하는 '규약' 역할
 - 예. '성별' 속성의 도메인은 {남, 여}입니다. (이 외의 값은 입력 불가), '점수' 속성의 도메인이 0~100 사이의 정수라면, 105점은 입력될 수 없습니다.

 

 

3. 다음은 정보보안 관련 문제이다. 아래 내용을 보고 알맞는 단어를 작성하시오.

- 원격 접속과 관련된 보안 프로토콜이며 암호화된 통신을 제공하는 보안 접속용 프로토콜이다.
- 공개키 기반의 인증 방식을 사용하며 암호화된 데이터 전송을 지원한다.
- 주로 원격 서버에 안전하게 접속할 때 사용되며 기본 포트 번호는22번이다.
- Telnet의 보안 취약점을 보완한 대안으로 널리 사용된다.
답: SSH
                               

SSH (Secure SHell, 시큐어 셀)
SSH는 다른 컴퓨터에 로그인, 원격 명령 실행, 파일 복사 등을 수행할 수 있도록 다양한 기능을 지원하는 프로토콜 또는 이를 이용한 응용 프로그램
 - 데이터 암호화와 강력한 인증 방법으로 보안성이 낮은 네트워크에서도 안전하게 통신할 수 있다.
 - 키(key)를 통한 인증 방법을 사용하려면 사전에 클라이언트의 공개키를 서버에 등록해야 한다.
 - 기본적으로 22번 포트를 사용한다.

HTTPS : 웹 브라우저 보안, SSL/TLS
 - 443 포트
 - HTTP에 SSL/TLS를 더한 보안 프로토콜 

SFTP : SSH 기반, 안전한 FTP
 - 22 포트
 - SSH의 보안 기능을 활용한 파일 전송 

SNMP : TCP/IP 네트워크 관리
 - 161/162 포트
 - 네트워크 장비를 관리/모니터링하는 프로토콜 

IPsec : VPN, 가상 사설망, 망 계층 보안
 - IP 계층(3계층)에서 암호화 및 인증 제공 

SSL/TLS : 인증서, 데이터 기밀성
- 응용 계층과 전송 계층 사이의 보안 계층 

 

 

4. 스케줄링 알고리즘에 관한 다음 설명을 읽고(1)과(2)에 알맞은 스케줄링 알고리즘의 명칭을 각각 쓰시오.

- (1) CPU burst시간이 짧은 프로세스를 우선적으로 처리하는 스케줄링 방식이다."Shortest Next CPU Burst"라고도 불리며 선점형 또는 비선점형으로 구현될 수 있다.
- (2) 위의 스케줄링 방식을 선점형으로 구현한 형태로 실행 중인 프로세스보다 더 짧은burst시간을 가진 프로세스가 도착하면 현재 CPU를 선점한다.
답:
(1) SJF (Shortest Job First)
(2) SRT (Shortest Remaining Time)
                               

비선점 (Non-Preemptive) 스케줄링
 - 이미 할당된 CPU를 다른 프로세스가 강제로 빼앗을 수 없는 방식
 - 응답 시간 예측이 용이하며, 일괄 처리 방식에 적합
 - 종류: FCFS(First-Come First-Served), SJF(Shortest Job First), HRN(Highest Response-ratio Next), 우선순위, 기한부 등

선점 (Preemptive) 스케줄링
 - 하나의 프로세스가 CPU를 할당받아 실행 중이더라도 우선순위가 높은 다른 프로세스가 CPU를 강제로 빼앗을 수 있는 방식
 - 빠른 응답을 요구하는 대화형 시분할 시스템에 적합
 - 종류: SRT(Shortest Remaining Time), RR(Round Robin), 다단계 큐다단계 피드백 큐 등

 

 

5. 다음은 Java의 문제이다. 아래 코드를 보고 알맞는 출력값을 작성하시오.

public class Main {
    public static void change(String[] data, String s) {
        data[0] = s;
        s = "Z";
    }
    public static void main(String[] args) {
        String data[] = { "A" };
        String s = "B";
        change(data, s);
        System.out.print(data[0] + s);
    }
}
답: BB
                               

public class Main {
    public static void change(String[] data, String s) {
→ main에서 보낸 data 주소와 "B"라는 값을 받았다.

        data[0] = s;
→ 전달받은 data 주소로 가서 data[0]을 s로 바꿔라
→ 메인에 있는 data[0]도 s값으로 바꿈
→ s값은 "B"를 전달 받아왔으므로, 메인에 있는 data[0]의 값은 A에서 B로 바뀐다.

        s = "Z";
→ change 함수 안에 있는 s라는 변수에 "Z"를 넣어라.
→ 이건 change 함수 안에서만 쓰는 복사본 s로, main에 있는 원래 s와는 상관 없음

→ 절차가 끝났으므로 다음 문장으로 이동 = System.out.print(data[0] + s);
                               

    public static void main(String[] args) {
        String data[] = { "A" };
String data[] = { "A" }; : String[] 배열을 만들고 첫 번째 칸(data[0])에 "A"를 넣어라.
→ data라는 변수는 실제 "A"가 든 주소를 가리키고 있음.

        String s = "B";
String s = "B"; : s라는 일반 변수에 "B"라는 글자 담음

        change(data, s);
→ 위에서 만든 배열 data 와 문자열 s를 가지고 change라는 함수(메소드)로 가라
→ data 주소값을 넘겨주는 것. s는 "B"값 자체를 복사해서 넘겨줌

                               

        System.out.print(data[0] + s);
→ data[0]은 "B"가 되었으므로 B가 출력
→ s의 경우에는 main함수에서 가져오는 것이므로 "B". B가 출력

(change에서 변경된 Z값을 가져오는 것이 아니다.)

그러므로 BB가 출력

 

 

6. 다음은 IP주소와 서브넷 마스크에 관한 문제이다. 주어진 정보를 참고하여 괄호 안에 들어갈 알맞은 값을 쓰시오.

호스트의IP주소가223.13.234.132이고서브넷 마스크가255.255.255.192일 때 다음 물음에 답하시오.
- 이 호스트가 속한 네트워크 주소는223.13.234.(①)이다.
- 이 네트워크에서 사용 가능한 호스트 수는(②)개이다.
- (단,네트워크 주소와 브로드캐스트 주소는 제외한다.)
답: ① 128 ② 62

 

 

7. 다음은 디자인 패턴에 관한 문제이다. 아래 내용을 보고 알맞는 단어를 작성하시오.

- 어떤 객체에 대한 접근을 제어하거나 추가적인 기능을 부여하기 위해해당 객체의 대리 객체를 사용하는 방식의 디자인 패턴이다. 실제 객체에 대한 접근 전에 필요한 작업을 수행할 수 있으며 실제 객체의 생성을 지연시켜 메모리와 자원을 절약할 수 있다. 또한,실제 객체를 감추어 정보은닉을 강화할 수 있다는 장점이 있다.
답: Proxy

 

 

8. 다음은 웹 데이터 교환 방식에 관한 문제이다. 아래 설명을 읽고괄호 안에 들어갈 알맞은 용어를 작성하시오.

- (     )은/는 웹 페이지 전체를 다시 불러오지 않고JavaScript와XML(또는JSON)을 이용하여 일부 콘텐츠만 비동기적으로 갱신할 수 있 는 기술이다.
- (     )은/는HTML만으로는 구현하기 어려운 동적인 기능들을 가능하게 하여 사용자가 웹 페이지와 보다 자유롭게 상호작용할 수 있도 록 해주는 웹 개발 기법이다.
답: AJAX
                               

AJAX
 - 비동기적(Asynchronous): 서버에 데이터를 요청하고 응답이 올 때까지 화면이 멈추지 않고 다른 작업을 할 수 있다
 - 일부 콘텐츠만 갱신: 전체 페이지를 새로고침(F5) 하지 않고, 좋아요 숫자나 댓글창만 슥 바뀌는 기술이 바로 AJAX입니다.

JSON
 - AJAX에서 데이터를 주고받을 때 쓰는 가벼운 텍스트 형식
 - 자바스크립트 객체 형태, XML 대체
(※AJAX의 이름에는 XML이 들어있지만, 실제 요즘 웹에서는 용량이 가벼운 JSON을 훨씬 더 많이 씁니다. 문제에서 "최근에는 XML 대신 이것을 주로 사용한다"라고 하면 정답은 JSON입니다.)

XML
 - 데이터를 정의하는 태그 기반 언어 (AJAX의 'X'가 이것)
 - 사용자 정의 태그, 확장성

REST
 - 웹의 장점을 최대한 활용하는 아키텍처 스타일
 - HTTP URI, 자원(Resource) 중심

SOAP
 - HTTP 등을 이용해 XML 기반 메시지를 교환하는 프로토콜
 - 봉투(Envelope) 구조, 오버헤드 큼

 

 

9. 다음은 Java 언어의 문제이다. 아래 코드를 보고 알맞는 출력값을 작성하시오.

public class Main {
    static interface F {
        int apply(int x) throws Exception;
    }
    public static int run(F f) {
        try {
            return f.apply(3);
        } catch (Exception e) {
            return 7;
        }
    }
    public static void main(String[] args) {
        F f = (x) -> {
            if (x > 2) {
                throw new Exception();
            }
            return x * 2;
        };
        System.out.print(
                run(f) +
                run((int n) -> n + 9)
        );
    }
}
답: 19
                               

public class Main {
    static interface F {
→ 인터페이스(intergace)는 이런 모양(F)으로 만들거야라고 정의

        int apply(int x) throws Exception;
int apply(int x) : int(정수)를 주면(int apply), 계산해서 다시 int(정수)를 준다(int x).
throws Exception; : 이 기능 안에서 에러가 발생하면, throw new Exception() 으로 간다는 뜻
→ 실기에서 throws, throw가 나오면 정상적인 계산 결과인지, 에러가 나서 catch문이 나올 것인지를 묻는 문제

                               

    public static int run(F f) {
→ 에러가 났을 때 대신 실행해준다.
run(F f) : f 라는 주문서 가져오면 대신 실행
→ f 자리에 run(f), run((int n) -> n + 9) 의 값이 들어가서, 아래에 계산이 되는 것

        try {
            return f.apply(3);
→ 가져온 기능에 숫자 3을 넣어서 결과를 뽑아내봐라
3은 2보다 크다. 그러므로 에러가 실행된다.
n+9에서 n에 3의 값을 넣는다.


        } catch (Exception e) {

            return 7;
f.appty(3)을 실행했는데도 에러가 나면, 7값으로 반환해라.
에러가 실행되므로 7값이 반환된다.
n+9에서는 에러가 발생하지 않으므로 무시

                               

    public static void main(String[] args) {
        F f = (x) -> {
→ F f 를 정의하겠다.
(x) : 숫자를 하나 받을건데, 그 숫자는 x라고 부르겠다.
-> { : x 값을 가지고 중괄호{} 안의 일을 시킬거다.

            if (x > 2) {
                throw new Exception();
→ 만약 받은 숫자 x가 2보다 크다면, 에러이므로 에러문을 실행한다.

            return x * 2;

→ 에러가 안 났다면, x값에 2를 곱해서 돌려준다.
→ 에러가 났다면, 실행되지 않는다.


        System.out.print(
                run(f) +
run(f) : 여기서 위(public static int run(F f) {)로 올라가서 실행된다. (※숫자가 2보다 크면 에러로 반환해라)
7이 된다.

                run((int n) -> n + 9)
run((int n) : 숫자 n 하나를 받는다.
-> n + 9) : 받은 숫자에 9를 더한다.
→ 해당 처리방법을 가지고 위(public static int run(F f) {)로 이동한다.
→ n + 9 에서 n에는 3값을 가지고 돌아온다. 즉, 3+9 = 12값이 된다.

그러므로 7+12 = 19가 출력된다.

 

 

10. 다음은 Java 언어의 문제이다. 아래 코드를 보고 알맞는 출력값을 작성하시오.

public class Main {
    public static class Parent {
        public int x(int i) {
            return i + 2;
        }
        public static String id() {
            return "P";
        }
    }
    public static class Child extends Parent {
        public int x(int i) {
            return i + 3;
        }
        public String x(String s) {
            return s + "R";
        }
        public static String id() {
            return "C";
        }
    }
    public static void main(String[] args) {
        Parent ref = new Child();
        System.out.println(ref.x(2) + ref.id());
    }
}
답: 5P
                               



Parnet ref = new Child();
Child 를 수행할 때, Parnet 의 변수도 사용할 수 있다.


ref.x(2) → Public int x(int i)
2는 정수이므로, 동일한 변수형태를 가진 곳으로 가서 계산을 수행한다.
그러므로 return i + 3 = 5 값이 리턴한다.
※좀 더 정확히는, Parent에 x(int)가 있는지 확인하고, 실제 실행은 Child의 x(int)에서 한다.

→ Child에 x(int)가 없었다면, 부모의 x(int)를 실행했다.

ref.id() → public static String id() → public static string id()
id()와 동일한 형태를 찾아갔다가, static 가 있으므로 parnet 를 호출해서 수행한다.
그러므로 return "P" = P 값이 리턴한다.

최종적으로 5P가 출력된다. (※ln 이 붙어있으므로 출력후 엔터쳐진다)

 

 

11. 다음 아래 제어 흐름 그래프가 분기 커버리지를 만족하기 위한 테스팅 순서를 쓰시오.

답: 1234561,124567
or 1234567,124561
                               

분기 커버리지 : 프로그램 내의 모든 조건문의 결과가 True인 경우와 False인 경우를 최소 한번씩은 실행하도록 테스트 경로를 구성하는 것
제어 흐름 그래프에 결정 지점(다이아몬드 박스)이 2번, 6번이므로
두 곳에서 YES와 NO를 모두 가보는 순서로 동작한다.

 

 

12. 다음은 C언어의 문제이다. 아래 코드를 보고 알맞는 출력값을 작성하시오.

#include <stdio.h>
#define SIZE 3
typedef struct {
    int a[SIZE];
    int front;
    int rear;
} Queue;
void enq(Queue* q, int val) {
    q->a[q->rear] = val;
    q->rear = (q->rear + 1) % SIZE;
}
int deq(Queue* q) {
    int val = q->a[q->front];
    q->front = (q->front + 1) % SIZE;
    return val;
}
int main() {
    Queue q = {{0}, 0, 0};
    enq(&q, 1);
    enq(&q, 2);
    deq(&q);
    enq(&q, 3);
    int first = deq(&q);
    int second = deq(&q);
    printf("%d 그리고 %d", first, second);
    return 0;
}
답: 2 그리고 3
                               

#include <stdio.h>
#define SIZE 3
typedef struct {
    int a[SIZE];
    int front;
    int rear;
→ 각 값을 설정한다. 
※큐(Queue)의 기본 규칙 : FIFO (선입선출)
 - front : 데이터가 나가는 문(출구)
 - rear : 데이터가 들어오는 문(입구)
                               

} Queue;
void enq(Queue* q, int val) {
    q->a[q->rear] = val;
    q->rear = (q->rear + 1) % SIZE;
→ 원형 큐라는 것을 알 수 있는 근거
enq(&q,1), enq(&q,2) 를 계산할 때 이곳으로 와서 계산된다.
enq(Queue* q : 여기서 q는 전달받은 &q(주소)를 저장하고 있다.
q->rear : 주소 q를 찾아가서 그 안에 있는 rear라는 칸에 적힌 숫자를 읽어라
→ &q를 주면, enq함수가 그걸 q라는 이름으로 받아서 q->rear를 조작한다.
                               

int deq(Queue* q) {
    int val = q->a[q->front];
    q->front = (q->front + 1) % SIZE;
    return val;
int val = q->a[q->front] : q의 출구 번호(front)가 가리키는 칸(a[ ])에 가서, 거기 있는 값을 임시 값(val)에 넣어라
q->front = (q->front + 1) % SIZE; : 값을 꺼냈으니, 출구 번호(front)를 다음 칸으로 옮겨라. (끝까지 갔으면 처음으로 돌아와라)
return val; : 임시 값에 있는 값을 리턴해서 가져가라

                               

int main() {
    Queue q = {{0}, 0, 0};
→ 초기 상태 설정 : q = {0, 0, 0} / front = 0, rear = 0

    enq(&q, 1);
enq(&q, 1) : q주소에 1을 넣는다.
q->a[q->rear] = val; : q -> a[0] = 1
q->rear = (q->rear + 1) % SIZE;  : rear = (0 + 1) % 3 = 1

    enq(&q, 2);
→ q -> a[1] = 2
→ rear = (1 + 1) % 3 = 2
※&q : enq함수를 실행할 때마다 'q -> rear'이란 내부 변수 값이 계속 변하기 때문에,  a[0] 다음인 a[1]이 호출되는 것
 - 첫 번째 enq(&q,1) 실행 → rear은 현재 0, a[0]에 1을 넣음 → rear은 한 칸 옆인 1로 옮겨감
 - 두 번째  enq(&q,2) 실행 → rear은 현재 1, a[1]에 2을 넣음 → rear은 한 칸 옆인 2로 옮겨감

    deq(&q);
int val = q->a[q->front]; : q->a[0] = 1 (enq로 1이 들어왔으니까), 1값을 임시 값(val)에 넣는다.
q->front = (q->front + 1) % SIZE; : (0 + 1) % 3 = 1, 1값을 q->front 에 넣는다.
return val; : 임시 값(val)에 들어간 1을 리턴한다.
(※main 함수에서 int x = deq(&q)처럼 넣은 게 아니라, 그냥 출구로 값을 보낸 것이기 때문에 1 값은 없어진다.)
(※데이터 1은 메모리 상에는 남아있을 수 있지만, front라는 출구 위치가 이미 1번 칸으로 이동했기 때문에, deq를 호출하면 그 뒷자리인 2부터 나오게 된다.)

    enq(&q, 3);
→ q -> a[2] = 3
→ rear = (2 + 1) % 3 = 0 (※0으로 바뀌기 때문에, 다시 초기로 돌아간다)

    int first = deq(&q);
deq(&q) : q->front가 1이므로 a[1]에 있는 값 2를 꺼내어 first 변수에 담는다.
front 는 다음 칸으로 옮긴다. (1 + 1) % 3 = 2

    int second = deq(&q);
deq(&q) : q->front가 2이므로 a[2]에 있는 값 3을 꺼내어 second 변수에 담는다.
front 는 다음 칸으로 옮긴다. (2 + 1) % 3 = 0

    printf("%d 그리고 %d", first, second);
first에 저장된 2와 second에 저장된 3이 서식에 맞춰 출력된다.

그러므로 출력내용 : 2 그리고 3
                               

※참고
q->rear는 그 자체로 q[0], q[1]이 되는 것이 아니라, q->a[ ]라는 이름의 선반 중 몇 번 칸을 가리킬지 정하는 손가락과 같습니다.
 - rear 값이 0이면 → q->a[0] 칸에 데이터를 넣습니다.
 - rear 값이 1이면 → q->a[1] 칸에 데이터를 넣습니다.
 - rear 값이 2이면 → q->a[2] 칸에 데이터를 넣습니다.

 

 

13. 라운드 로빈(RR)방식을 이용하고, 아래 내용을 참고하여 평균 대기 시간을 구하시오.

- 운영체제에서 라운드로빈(Round Robin, RR)스케줄링은 각 프로세스에 동일한 시간 할당량(타임 퀀텀)을 순차적으로 부여하며 CPU를 할당하는 방식이다.
- 다음은4개의 프로세스가 서로 다른 시간에 도착하며 각기 다른 실행 시간을 가지는 상황이다.이때 시간 할당량은 4ms이고 컨텍스트 스위칭 시간은 무시한다고 가정한다.
- 아래 정보를 바탕으로 라운드로빈(RR)방식으로CPU스케줄링을 수행할 경우 모든 프로세스의 평균 대기시간(Average Waiting Time)은 얼마인가?

답: 11.75ms
                               

시간 할당량 : 4ms
시간의 흐름에 따라 간트 차트 그리기
0 ~ 4ms : 0초에 P1만 있으므로 바로 시작, 4ms 실행 후, 실행시간 8ms에서 4ms가 남음
4 ~ 8ms : 순차적으로 부여하므로 P2 시작, 4ms 실행 후, 실행시간 4ms에서 0ms가 남음 = P2은 종료
8 ~ 12ms : 큐 다음 순서인 P3 시작, 4ms 실행 후, 실행시간 9ms에서 5ms가 남음
12 ~ 16ms : 큐 다음 순서인 P4 시작, 4ms 실행 후, 실행시간 5ms에서 1ms가 남음
16 ~ 20ms : 한 바퀴 돌아서 다시 P1 시작, 4ms 실행 후, 실행시간 4ms에서 0ms가 남음 = P1 종료
20 ~ 24ms : 큐 다음 순서인 P3 시작, 4ms 실행 후, 실행시간 5ms에서 1ms가 남음
24 ~ 25ms : 큐 다음 순서인 P4 시작, 남은 실행시간 1ms만 실행하고 종료 = P4 종료
25 ~ 26ms : 큐 다음 순서인 P3 시작, 남은 실행시간 1ms만 실행하고 종료 = P3 종료

간트차트 정리


각 프로세스의 종료 시간 및 대기 시간 계산
[종료시간 - 도착시간 - 실행시간]
P1: 20(종료) - 0(도착) - 8(실행) = 12ms
P2: 8(종료) - 1(도착) - 4(실행) = 3ms
P3: 26(종료) - 2(도착) - 9(실행) = 15ms
P4: 25(종료) - 3(도착) - 5(실행) = 17ms

평균 대기 시간
[각 큐별 대기시간/시간 할당량]
(12+3+15+17)/4 = 11.75

 

 

14. 다음은 C언어의 문제이다. 아래 코드를 보고 알맞는 출력값을 작성하시오.

#include <stdio.h>
struct dat {
    int x;
    int y;
};
int main() {
    struct dat a[] = {{1,2}, {3,4}, {5,6}};
    struct dat* ptr = a;
    struct dat** pptr = &ptr;
    (*pptr)[1] = (*pptr)[2];
    printf("%d 그리고 %d", a[1].x, a[1].y);
    return 0;
}
답: 5 그리고 6
                               

#include <stdio.h>
struct dat {
    int x;
    int y;
→ 배열의 초기 상태 정의


int main() {
    struct dat a[] = {{1,2}, {3,4}, {5,6}};
 


    struct dat* ptr = a;
dat* ptr = a : ptr이라는 포인터가 배열의 시작점 a[0]을 가리킴


    struct dat** pptr = &ptr;
dat** pptr = &ptr; : pptr은 ptr이라는 포인터 변수의 주소를 가리킴. (포인터를 가리키는 포인터)
                               

    (*pptr)[1] = (*pptr)[2];
(*pptr) : ptr을 가리키고 있으므로 (*pptr) = ptr 과 같은 놈
(*pptr)[1] = (*pptr)[2]; : ptr[1] = ptr[2]; 같은 말이다.
→ ptr은 현재 배열 a[0]를 가리키고 있다.
→ ptr[1]은 a[1]을 의미하고, ptr[2]는 a[2[를 의미한다.
→ 즉, a[2]의 값을 a[1]에 넣으라는 뜻


    printf("%d 그리고 %d", a[1].x, a[1].y);
a[1].x : a[1]의 x값은 5
a[1].y) : a[1]의 y값은 6

그러므로 출력값은 5 그리고 6

 

 

15. 다음은 Java 언어의 문제이다. 아래 코드를 보고 알맞는 출력값을 작성하시오.

public class Main {
    public static class BO {
        public int v;
        public BO(int v) {
            this.v = v;
        }
    }
    public static void main(String[] args) {
        BO a = new BO(1);
        BO b = new BO(2);
        BO c = new BO(3);
        BO[] arr = { a, b, c };
        BO t = arr[0];
        arr[0] = arr[2];
        arr[2] = t;
        arr[1].v = arr[0].v;
        System.out.println(a.v + "a" + b.v + "b" + c.v);
    }
}
답: 1a3b3
                               

public class Main {
    public static class BO {
        public int v;
        public BO(int v) {
            this.v = v;
        }
    }
    public static void main(String[] args) {
        BO a = new BO(1);
new BO(1) 은 생성자를 찾아간다. (public static class BO {)
public BO(int v) { : (int v)에 배달된 1을 받는다. 이때 int v는 외부에서 들어온 값
this.v : this.v 는 내(BO)가 진자로 가지고 있는 값.
this.v = v; : 그러므로 외부에서 들어온 값을 나(BO)에게 저장하겠다.
→ BO a의 a는 BO(1) 의 1을 가리키고 있다.
                               

        BO b = new BO(2);
→ b는 2를 가리키고 있다.

        BO c = new BO(3);
→ c는 3을 가리키고 있다.

        BO[] arr = { a, b, c };

→ a,b,c가 가리키는 객체들로 배열을 만든다.

                               

        BO t = arr[0];
→ t 는 이제부터 arr[0]이 가리키던 것과 같은 것을 가리킨다.
→ 즉, t는 객체명 a 와 값 1을 가리키게 된다.


        arr[0] = arr[2];
→ arr[0] 은 이제부터 객체명 c를 가리키고, 값 3을 가리킨다.
(※arr[0]에 있는 값이 바뀌는 것이 아니라, arr[0]이 가리키는 것이 arr[2]와 같아진 것)



        arr[2] = t;
→ arr[2] 은 이제부터 t 가 가리키는 것을 가리키게 된다.
→ t 는 객체명 a와 값 1을 가리키고 있었으므로, 이제부터 arr[2]도 가리키게 된다.


        arr[1].v = arr[0].v;
arr[1].v : arr[1] 은 객체 b, 값(v) 2 를 가리키고 있다.
arr[0].v : arr[0] 은 객체 c, 값(v) 3 을 가리키고 있다.
arr[1].v = arr[0].v; : 즉, arr[1]의 값(v)에 arr[0]의 값(v)을 넣어라

                               

        System.out.println(a.v + "a" + b.v + "b" + c.v);
a.v : 1 값을 가지고 있음
"a" : 문자 a
b.v : 3 값을 가지고 있음
"b" : 문자 b
c.v : 3 값을 가지고 있음
(※객체가 가지고 있는 값은 b말고는 변화된 적이 없음.)


그러므로 1a3b3 이 된다.

 

 

16. 다음은 C언어의 문제이다. 아래 코드를 보고 알맞는 출력값을 작성하시오.

#include <stdio.h>
#include <stdlib.h>
struct node {
    int p;
    struct node* n;
};
int main() {
    struct node a = {1, NULL};
    struct node b = {2, NULL};
    struct node c = {3, NULL};
    a.n = &b;
    b.n = &c;
    c.n = NULL;
    c.n = &a;
    a.n = &b;
    b.n = NULL;
    struct node* head = &c;
    printf("%d%d%d",
           head->p,
           head->n->p,
           head->n->n->p);
    return 0;
}
답: 312 (공백없이)
                               

#include <stdio.h>
#include <stdlib.h>
struct node {
    int p;
    struct node* n;
→ 노드의 구성을 정의

                               

int main() {
    struct node a = {1, NULL};
    struct node b = {2, NULL};
    struct node c = {3, NULL};
→ 메모리에 3개의 노드가 만들어진다.

                               

    a.n = &b;
→ a.n(node*n) 은 b주소를 가리킨다.

    b.n = &c;
→ b.n(node*n) 은 c주소를 가리킨다.

    c.n = NULL;
→ c.n(node*n) 은 NULL을 가리킨다.


    c.n = &a;
    a.n = &b;
    b.n = NULL;
    struct node* head = &c;
→ 노드의 시작점을 c의 주소로 정하겠다. 즉, head란 이름표는 c를 보고 있다.
struct node* : node 구조체를 가리키는 포인터(리모컨)
head : 포인터(리모컨)의 이름
&c : 노드 c가 있는 주소를 가리켜라.


                               

    printf("%d%d%d",
           head->p,
→ (파란색) p값을 가리키니까 3

           head->n->p,
→ (초록색) n->p값을 가리키니까 1

           head->n->n->p);
→ (초록색) n->n->p값을 가리키니까 2


그러므로 312 가 된다. (공백없이)

 

 

17. 다음은 Pyhon 언어의 문제이다. 아래 코드를 보고 알맞는 출력값을 작성하시오.

lst = [1, 2, 3]
dst = {i: i * 2 for i in lst}
s = set(dst.values())
lst[0] = 99
dst[2] = 7
s.add(99)
print(len(s & set(dst.values())))
답: 2
                               

lst = [1, 2, 3]
→ 리스트가 만들어진다.

dst = {i: i * 2 for i in lst}
딕셔너리 컴프리헨션 : 반복문을 한 줄로 줄여 쓴 것
for i in lst : lst에 있는 값을 i 에 넣어라 (※키:값 형태로 딕셔너리에 들어간다.)
i: i * 2 : i는 i*2를 해라
dst = 나온 값을 dst에 넣어라

→ 1이 들어오면 1:1*2 → {1:2}
→ 2이 들어오면 2:2*2 → {1:2 , 2:4}
3이 들어오면 3:2*2 → {1:2 , 2:4, 3:6}


s = set(dst.values())
dst.values() : 딕셔너리에서 값(values)만 가져와라
{1:2 , 2:4, 3:6} → [2, 4, 6] 이 된다
set(dst.values()) : 중복을 허용하지 않는 세트(주머니)로 만든다.
→ s = {2, 4, 6} 이 된다.


lst[0] = 99
→ lst의 첫 번째 값을 바꾼다.
lst = [1, 2, 3] → lst = [99, 2 ,3]
(※lst와 dst는 다른, 독립적인 딕셔너리이므로 lst를 바꿔도 dst는 안 바뀐다.)

dst[2] = 7
→ dst에서 키가 2인 요소의 값을 7로 바꾼다.
dst = {1:2 , 2:4, 3:6}dst = {1:2 , 2:7, 3:6}

s.add(99)
→ s에서 99값을 추가(add)해라.
s = {2, 4, 6} → s = {2, 4, 6, 99

print(len(s & set(dst.values())))
set(dst.values()) : dst의 값(values)은 현재 2,7,6 → 이걸 세트로 만들면 {2,6,7}

s : s의 값은 현재 [2,4,6,99]
s & set(dst.values()) : 교집합 → {2,4,6,99} & {2,6,7} → 공통된 요소는 2, 6
len(s & set(dst.values())) : 길이(len) 측정 → 2,6이므로 길이는 2

그러므로 2
※ [ ] : 리스트, 순서가 있고 값을 마음대로 바꿀 수 있음
※ { } : 딕셔너리, '키:값' 쌍으로 저장. 키를 알면 값을 바로 찾음
※ { } : 세트, 순서가 없고, 중복을 절대 허용 안 함
※ ( ) : 튜플, 순서가 있지만, 한 번 정하면 절대 못 바꿈

 

 

18. 다음은 C언어의 문제이다. 아래 코드를 보고 알맞는 출력값을 작성하시오.

#include <stdio.h>
#include <stdlib.h>
struct node {
    char c;
    struct node* p;
};
struct node* func(char* s) {
    struct node* h = NULL;
    struct node* n;
    while (*s) {
        n = malloc(sizeof(struct node));
        n->c = *s++;
        n->p = h;
        h = n;
    }
    return h;
}
int main() {
    struct node* n = func("BEST");
    while (n) {
        putchar(n->c);
        struct node* t = n;
        n = n->p;
        free(t);
    }
    return 0;
}
답: TSEB
                              

#include <stdio.h>
#include <stdlib.h>
struct node {
    char c;
    struct node* p;
→ 노드(node)의 형태 정의

                              

struct node* func(char* s) {
→ main 함수에서 'struct node* n = func("BEST")'의 "BEST"값을 받는다.

    struct node* h = NULL;
→ node* h 에 NULL 값을 넣는다.

    struct node* n;
→ node* n : 앞으로 노드를 만들 때마다 임시 리모컨 n 을 만들겠다.
struct node* n = func("BEST") : "BEST" 를 받겠다.

    while (*s) {
→ B, E, S, T 가 끝날때까지 반복한다.

        n = malloc(sizeof(struct node));
malloc : 컴퓨터 메모리라는 빈 땅에 내가 쓸 공간을 줘
sizeof(struct node) : struct node 의 크기(sizeof)만큼
n = : 새로 지어진 주소를 포인터(리모컨) n에 저장


        n->c = *s++;
n : 리모컨
-> : 리모컨이 가리키는 집 안으로 들어가라
c : 집 안에 있는 방이름
n->c 는 방금 만든 노드(n)의 c 라는 칸을 가리킨다. (가리키고 있을 뿐)
*s : 현재 s값이 가리키는 값, "B"를 가져옴
s++ : "B"를 줬으니 이제 다음 글자인 "E"를 가리키도록 보따리 위치를 한 칸 옆으로 옮긴다.

*s++ : s에 들어있는 값(BEST)에서 글자 하나를 꺼내서 다음 글자로 손가락을 옮겨라

        n->p = h;
n->p : n 이 가리키는 노드의 p칸을 열어라
= : 오른쪽에 있는 값을 왼쪽에 복사해서 넣어라
h : 지금 h가 기억하고 있는 주소 'struct node* h = NULL;' 했기 때문에 NULL 이 들어간다.


        h = n;
→ h 의 값에 n 의 값을 넣어라. (h가 이제부터 n이 가리키는 곳을 가리켜라)
→ n 은 현재 1회전한 'B, NULL' 의 주소이다.


→ 이를 반복한다.
반복하게 되면, 



    }

    return h;
→ 반복해서 얻은 h의 값을 리턴해라. 가장 마지막에 얻은 T의 주소값이 리턴됨
                              

int main() {
    struct node* n = func("BEST");
→ 위의 'struct node* func(char* s)'를 호출한다.
→ func라는 곳에 가서 "BEST"를 주고 결과를 받아온다.


    while (n) {
→ 위('return h')에서 얻은 T의 주소값이 n으로 리턴된다.
→ T의 주소값이 NULL을 만날때까지 반복된다.


        putchar(n->c);
putchar : 출력해라
→ T 주소에 있는 c의 값을 출력해라 = T

        struct node* t = n;
struct node* t : t (Temporary)는 임시값을 뜻함.
→ T의 값을 임시로 저장해둬라.

        n = n->p;
→ n의 p 값을 n에 넣어라

        free(t);
free : 삭제해라
→ t의 값을 삭제해라

즉, T가 출력되고, 리모컨은 S로 이동. 이때 T의 노드는 삭제된다.
다음 S 출력, 리모컨은 E로 이동, S 노드 삭제
다음 E 출력, 리모컨은 B로 이동, E 노드 삭제
다음 B 출력, 리코먼은 NULL로 이동, B노드 삭제
n 이 NULL 이므로 반복문 종료

즉, TSEB 출력

 

 

19. 다음은 TCP통신 과정에서 발생할 수 있는 보안 취약점에 대한 설명이다. 이를 이용한 공격 기법으로 옳은 것은?

- TCP는 연결을 수립하기 위해 클라이언트가 서버에 SYN패킷을 보내고 서버는 SYN-ACK 패킷으로 응답한 후클라이언트가 다시 ACK 패킷을 보내는 3-way-handshake과정을 거친다.
- 이때 공격자는 클라이언트 역할로 수많은 SYN패킷을 서버에 전송한 뒤 마지막 ACK를 고의로 보내지 않아 서버가 연결 대기 상태를 계속 유지하게 만든다.
- 이로 인해 서버의 연결 대기 큐가 가득 차면서 정상적인 접속 요청을 처리하지 못하게 되어 서비스 거부 상태가 발생한다.
답: SYN Flooding
                               

1. DoS (서비스 거부 공격) 계열 : 시스템의 자원을 고갈시켜 정상적인 서비스를 방해하는 공격입니다.
Smurf (스머프)
 - IP 위조(Spoofing)와 ICMP Echo 패킷을 이용합니다.
 - 공격자가 다이렉트 브로드캐스트를 통해 수많은 컴퓨터에 패킷을 보내면, 응답 패킷이 모두 희생자(Victim)에게 집중되게 만듭니다.

Ping of Death
 - 허용 범위를 넘어서는 거대한 크기의 Ping 패킷을 전송합니다.
 - 패킷이 네트워크를 통과하기 위해 여러 파편(Fragment)으로 나뉘고, 수신 측에서 이를 재조합하는 과정에서 부하가 발생하여 시스템이 다운됩니다.

Land Attack
 - 패킷을 보낼 때 출발지(Source) IP와 목적지(Destination) IP를 희생자의 IP로 동일하게 설정하여 전송합니다.
 - 희생자는 자신에게 온 패킷을 다시 자신에게 응답하게 되어 무한 루프에 빠집니다.

Teardrop
 - IP 패킷의 재조합 과정에서 사용하는 오프셋(Offset) 값을 조작합니다.
 - 조각난 패킷들이 서로 중첩되거나 빈 공간이 생기게 하여 수신 측 시스템을 오류로 마비시킵니다.
                               
2. 세션 및 스니핑 관련 공격 : 데이터를 훔쳐보거나 연결 권한을 가로채는 공격입니다.
Session Hijacking (세션 하이재킹)
 - 클라이언트와 서버가 통신 중일 때, TCP Sequence Number를 조작하여 인증된 세션을 중간에서 가로채는 공격입니다.
 - "세션 가로채기"라고도 합니다.

Sniffing
 (스니핑)
 - 네트워크상에서 흘러 다니는 패킷을 몰래 엿보는 공격입니다.
 - 수동적인 공격에 해당하며, 네트워크 카드를 'Promiscuous Mode(무차별 모드)'로 설정하여 작동합니다.

ARP Spoofing
 - 로컬 네트워크(LAN)에서 ARP 메시지를 조작하여, 공격자의 MAC 주소를 게이트웨이나 타겟의 주소인 것처럼 속입니다.
 - 이를 통해 중간에서 패킷을 가로챌 수 있습니다.
                               
3. 애플리케이션 및 코드 취약점 공격 : 소프트웨어 설계상의 허점을 파고드는 공격입니다.
SQL Injection
 - 웹 사이트의 입력창에 악의적인 SQL 구문을 삽입하여 데이터베이스(DB)를 조작하거나 비정상적인 로그인을 시도하는 공격입니다.

XSS
 (Cross-Site Scripting)
 - 검증되지 않은 외부 입력을 웹 페이지에 포함시켜 사용자의 브라우저에서 악성 스크립트가 실행되게 하는 공격입니다. (사용자의 쿠키 탈취 등에 사용)

Buffer Overflow
 - 메모리의 정해진 크기(버퍼)보다 더 큰 데이터를 입력하여 인접한 메모리 영역을 덮어쓰고, 복귀 주소(Return Address)를 조작해 공격자의 코드를 실행시키는 기법입니다.

 

 

20. 다음 테이블에서 πTTL(employee)에 대한 연산 결과 값을 작성하시오.

답:
1. TTL
2. 부장
3. 대리
4. 과장
5. 차장
                               

πTTL(employee)
π : project 연산을 의미하며, 특정 속성(TTL)만 출력해라.
TTL : 특정 속성