Part1. 게임 프로그래밍 방법론: 1~25
2025 게임 프로그래밍 전문가 국가기술 자격검정 필기시험 B 풀이 | Part1. 게임 프로그래밍 방법론: 1~25
Part 1. 게임 프로그래밍 방법론
1. 아래 코드는 표준 C++언어로 구현한 코드다. 코드의 실행결과로 올 바른 것은?
#include <iostream>
using namespace std;
class Parent {
public:
Parent() { cout << "Parent-"; }
~Parent() { cout << "~Parent-"; }
};
class Child : public Parent {
public:
Child() { cout << "Child-"; }
~Child() { cout << "~Child-"; }
};
void func(){
Child obj;
}
int main(){
cout << "Start-";
func();
cout << "End";
return 0;
}
① Start-Parent-Child-End~Child-~Parent-
② Start-Parent-Child-~Child-~Parent-End
③ Parent-Child-~Child-~Parent-Start-End
④ Start-Child-Parent-~Parent-~Child-End
개념: 생성자 (Constructor), 소멸자 (Destructor), 상속 (Inheritance)
풀이:
- main 시작이 cout << "Start-"; 니깐 ③ 은 제외.
- cout << "End"; 가 없는 ① 제외.
- ② & ④ 남음.
- func() 에서는 Child 인 obj 를 생성.
- Child 는 Parent 를 상속 받았기에 Parent의 생성자 → Child의 생성자
- 소멸시 반대로 Child 소멸자 → Parent 소멸자
정답: ② Start-Parent-Child-~Child-~Parent-End
2. 다음 C++ 코드를 실행한 결과로 올바른 것은?
#include <iostream>
using namespace std;
int main() {
int a = 10;
int *p = &a;
int **pp = &p;
**pp = 20;
*p = *p + 10;
cout << a << endl;
return 0;
}
①10
②20
③30
④4
개념: 포인터 (Pointer)
풀이:
- int a =10; // int a의 값은 10
- int *p = &a; // int *p 는 a의 주소를 갖는다
- int **pp = &p; // int **p 는 a의 주소를 갖고 있는 *p 의 주소를 갖는다 // 10 → a → p → PP
- **pp = 20; // *p, a 다 20 으로 변경
- *p = *p +10; // 20 이였던 *p 가 → 30 으로 변경 → 그로인해 a 도 30으로 변경
정답: ③30
3. 아래 코드의 Ⓐ 라인에서 calculate()함수를 올바르게 선언한 것은?
#include <iostream>
using namespace std;
Ⓐ // 함수 선언
{
return a + b + c;
}
int main() {
cout << calculate(1) << endl;
cout << calculate(1, 2) << endl;
cout << calculate(1, 2, 3) << endl;
return 0;
}
출력결과
16
13
6
①int calculate(int a, int b = 5, int c = 10)
②int calculate(int a = 0, int b, int c = 10)
③int calculate(int a, int b, int c = 10)
④int calculate(int a = 0, int b = 5, int c)
개념: 디폴트 매개변수 (Default Parameter)
풀이:
- 매개변수가 1~3 개 까지 들어 올 수 있기 때문에, 2 번째, 3번째는 디폴트 매개변수가 선언 되어 있어야함
- ② & ③ & ④ 다 선언 안되어 있기 때문에 아웃
- 거기다가 ④ 의 경우 b 는 선언 했는데 뒤에오는 c 는 선언 안했는데, 이 경우는 아에 컴파일 에러가 뜬다.
- ② & ③ & ④ 다 선언 안되어 있기 때문에 아웃
확인:
- cout << calculate(1) << endl; // 1 + 5 (디폴트 매개변수) + 10 (디폴트 매개변수) → 16
- cout << calculate(1, 2) << endl; // 1+2+10 (디폴트 매개변수) → 13
- cout << alculate(1, 2, 3) << endl; // 1+2+3→ 6
정답: ①int calculate(int a, int b = 5, int c = 10)
4. 아래 코드의 Ⓐ 라인에 들어갈 수 있는 클래스 생성자로 올바르게 작성된 것은?
#include <iostream>
using namespace std;
class Box {
private:
double length;
const double width; // const 멤버 변수
double& height; // 참조 멤버 변수
public:
Ⓐ // 생성자
double volume() {
return length * width * height;
}
};
int main() {
double h1 = 1.0, h2 = 1.0, h3 = 5.0;
Box box1(h1);
Box box2(h2, 2);
Box box3(h3, 3, 4);
cout << box1.volume() << endl;
cout << box2.volume() << endl;
cout << box3.volume() << endl;
return 0;
}
출력 결과
1
2
60
①Box(double& h) { length = 1; width = 1; height = h; }
②Box(double l, double& h) { length = l; width = 1; height = h; }
③Box(double& h, double l = 2, const double w = 3) : length(l), w idth(w), height(h) {}
④Box(double& h, double l = 1, const double w = 1) : length(l), w idth(w), height(h) {}
개념: 클래스 생성자
풀이: 3번 문제와 비슷,
- 1,2 번은 컴파일 에러 (멤버를 초기화하는 멤버 이니셜라이저 목록 때문)
- 3번은 출력결과가 틀리게 나오기 때문에 4번 정답
- 참고: https://learn.microsoft.com/ko-kr/cpp/cpp/constructors-cpp?view=msvc-170
정답: ④Box(double& h, double l = 1, const double w = 1) : length(l), width(w), height(h) {}
5. 다음은 C++언어로 작성된 코드로, 함수 다형성과 오버라이딩에 관한 문제다. 컴파일 할 때 에러가 발생하는 라인을 모두 찾은 것은?
#include <iostream>
using namespace std;
class Parent {
public:
virtual void show() { cout << "Parent "; }
};
class Child : public Parent {
public:
void show() { cout << "Child "; }
void show() { cout << "Child-show"; } // Ⓐ 컴파일에러
void display() { cout << "Display Child"; }
};
class Derived : public Parent {
public:
void print() { cout << "Derived"; }
};
int main() {
Parent* p = new Derived();
p->print(); // Ⓑ 컴파일에러
Parent* q = new Child();
q->show(); // Ⓒ 컴파일에러
delete p;
delete q;
return 0;
}
①정상적으로 컴파일 됨 ②Ⓐ ③Ⓐ Ⓑ ④Ⓐ Ⓑ Ⓒ
개념: 함수재정의, 상속
에러 원인 분석
- Ⓐ void show() { cout << "Child-show"; } (Child 클래스 내부) ◦ 이유: Child 클래스 내부에 동일한 시그니처(void 반환형, 매개변수 없음)를 가진 show() 함수가 두 번 정의되었습니다. C++에서는 같은 범위 내에서 이름과 매개변수가 완전히 같은 함수를 재정의(Redefinition)할 수 없어 컴파일 에러가 발생합니다. ◦ 참고: void show() { ... }가 virtual`이더라도, 동일 클래스 내의 동일 시그니처 함수 중복은 허용되지 않습니다.
- Ⓑ p->print(); (main 함수 내부) ◦ 이유: Parent* p = new Derived();로 선언되어 p는 Parent 클래스 타입의 포인터입니다. Parent 클래스에는 print()라는 멤버 함수가 정의되어 있지 않습니다. 컴파일러는 Parent의 인터페이스를 기반으로 print()를 찾을 수 없으므로 에러를 발생시킵니다.
- Ⓒ q->show(); (main 함수 내부 - 정상) ◦ 이유: Parent* q = new Child();에서 q는 Parent 타입의 포인터이지만, show()는 Parent 클래스에서 virtual로 선언되어 있습니다. 런타임에 실제 객체인 Child의 show() 함수가 호출되는 다형성이 정상적으로 동작합니다. [1, 2, 3, 4, 5]
풀이
Ⓐ: 동일 시그니처 함수 중복 정의 (오버로딩 불가)
Ⓑ: 부모 클래스 포인터로 자식 클래스의 멤버 호출 불가 (상속 구조 내에 함수 없음)
정답: ③Ⓐ Ⓑ
6. 다음 코드에서 Ⓐ 라인에 들어갈 적절한 코드와 이 코드의 실행 결
과를 올바르게 짝지은 것은?(메모리 누수가 발생하면 안됨)
#include <iostream>
using namespace std;
int main() {
Ⓐ // 동적 메모리 할당
for(int i = 0; i < 5; i++) {
arr[i] = i * 10;
}
int* ptr = arr + 2; // arr의 세 번째 요소를 가리키는 포인터
*ptr = 99; // 값 변경
ptr[-1] = 55; // 포인터 산술 연산을 사용한 할당
for(int i = 0; i < 5; i++) {
cout << arr[i] << " ";
}
cout << endl;
delete[] arr;
retu
①int* arr = new int[5]; 출력 결과: 0 55 99 30 40 ②int* arr = new int[5]; 출력 결과: 55 99 20 30 40 ③int* arr = (int*)malloc(5 * sizeof(int)); 출력 결과: 0 55 99 30 40 ④int* arr = new int[5]; 출력 결과: 0 10 99 30 40
개념: 포인터, 메모리, 배열
풀이
1. Ⓐ에 들어갈 코드
메모리 누수가 발생하지 않으려면 new[]로 할당하고 delete[]로 해제해야 합니다.for 문에서 arr[i] 형태의 인덱싱을 사용하므로, arr은 int* 형식의 포인터 변수여야 합니다. • 코드: int* arr = new int[5];
2. 코드 실행 흐름 및 결과 분석
- 할당: int* arr = new int[5]; (0번지~4번지, 총 5개 int 공간)
- 초기화: for문 실행 ◦ arr[0]=0, arr[1]=10, arr[2]=20, arr[3]=30, arr[4]=40 ◦ 배열 상태: {0, 10, 20, 30, 40}
- 포인터 이동: int* ptr = arr + 2; ◦ ptr은 &arr[2] (세 번째 요소)를 가리킴.
- 값 변경 1: *ptr = 99; ◦ arr[2]의 값이 99로 변경됨. ◦ 배열 상태: {0, 10, 99, 30, 40}
- 값 변경 2: ptr[-1] = 55; ◦ ptr이 arr[2]이므로 ptr[-1]은 *(ptr - 1)을 의미, 즉 arr[1]을 가리킴. ◦ arr[1]의 값이 55로 변경됨. ◦ 배열 상태: {0, 55, 99, 30, 40}
- 출력: for문을 돌며 arr[i] 출력 ◦ 결과: 0 55 99 30 40
- 메모리 해제: delete[] arr; (정상 해제)
3. 올바른 선택지 • Ⓐ 코드: int* arr = new int[5]; • 출력 결과: 0 55 99 30 40 따라서, ①번이 올바른 짝입니다.
- 3번인 int* arr = (int*)malloc(5 * sizeof(int)); 경우 free(arr) 을 통해서 Memory Release 가 없기 때문에 틀림.
정답: ①int* arr = new int[5]; 출력 결과: 0 55 99 30 40
7. 다음 C 코드에서 Ⓐ에 알맞은 코드는 무엇인가?
#include <stdio.h>
void print_even(int num) {
if (num % 2 == 0) printf("%d is even\\n", num);
}
void print_odd(int num) {
if (num % 2 != 0)
printf("%d is odd\\n", num);
}
void callback(int arr[], int size, Ⓐ ) { // Ⓐ 알맞은 코드는?
for (int i = 0; i < size; i++) {
fp(arr[i]);
}
}
int main() {
int arr[5] = { 1, 2, 3, 4, 5 };
callback(arr, 5, print_even);
callback(arr, 5, print_odd);
return 0;
}
① void (*fp)(int)
② void fp(int)
③ int (*fp)(int)
④ void *fp(int)
개념: 함수 포인터
풀이
- 함수 포인터 선언: callback 함수에서 print_even과 print_odd 함수를 인자로 받아 fp(arr[i]) 형태로 호출하고 있습니다. 이 함수들은 void를 반환하고 int형 매개변수 하나를 받는 형태(void func(int))입니다.
- 문법: 함수 포인터를 선언할 때는 반환형 (*포인터명)(매개변수타입) 형식을 사용해야 합니다. ◦ 반환형: void ◦ 포인터명: fp ◦ 매개변수 타입: int
- 따라서, void (*fp)(int)가 올바른 함수 포인터 선언입니다. • ② void fp(int): 함수 포인터가 아닌 void를 반환하는 fp라는 이름의 일반 함수 선언입니다. • ③ int (*fp)(int): 반환형이 int인 함수 포인터입니다. • ④ void *fp(int): void 포인터를 반환하는 함수 포인터입니다.
정답: ① void (*fp)(int)
8. 다음과 같은 매크로 정의가 있을 때, 출력 결과를 올바르게 예측한 것은?
#include <iostream>
using namespace std;
#define SQUARE(x) x * x
#define CUBE(x) SQUARE(x) * x
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define PRINT(x) cout << #x << " = " << x << endl
int main() {
int a = 5;
int b = 3;
PRINT(SQUARE(a+1));
PRINT(CUBE(a));
PRINT(MAX(a++, b));
PRINT(a);
return 0;
}
①SQUARE(a+1) = 36 CUBE(a) = 125 MAX(a++, b) = 5 a = 6
②SQUARE(a+1) = 11 CUBE(a) = 125 MAX(a++, b) = 5 a = 6
③SQUARE(a+1) = 11 CUBE(a) = 75 MAX(a++, b) = 6 a = 7
④SQUARE(a+1) = 11 CUBE(a) = 125 MAX(a++, b) = 6 a = 7
개념: 매크로
코드 분석
- #define SQUARE(x) x * x ◦ PRINT(SQUARE(a+1)); \(\rightarrow \) cout << "a+1 * a+1" << " = " << a+1 * a+1 << endl; ◦ \(a=5\)이므로 5+1 * 5+1 = \(5+5+1 = 11\) (연산자 우선순위) [매크로 함수의 '단순 치환(Text Substitution)' 성질과 '연산자 우선순위']
SQUARE(a+1)이 36이 아니라 11이 되는 이유는 매크로 함수의 '단순 치환(Text Substitution)' 성질과 '연산자 우선순위' 때문입니다.**1. 매크로는 계산을 먼저 하지 않습니다.**일반적인 함수는 a+1을 계산해서 6이라는 값을 전달하지만, 매크로는 괄호가 없으면 소스 코드에 적힌 글자 그대로를 가져다 붙입니다. • 매크로 정의: #define SQUARE(x) x * x • 코드 호출: SQUARE(a+1)여기서 x 자리에 a+1이 그대로 들어갑니다.2. 치환된 결과컴퓨터가 실제로 계산하게 되는 식은 다음과 같습니다.a + 1 * a + 13. 연산자 우선순위 적용C++에서 곱셈(*)은 덧셈(+)보다 우선순위가 높습니다. 따라서 a=5일 때 식은 다음과 같이 풀립니다.
- 5 + 1 * 5 + 1
- 5 + (1 * 5) + 1 <-- 곱셈 먼저 계산
- 5 + 5 + 1
- 결과: 11
**만약 36이 나오게 하려면?**매크로 정의 시 인자마다 괄호를 씌워주어야 합니다. • 수정된 정의: #define SQUARE(x) (x) * (x) • 치환 결과: (a+1) * (a+1) → (5+1) * (5+1) = 36이처럼 매크로를 정의할 때는 의도치 않은 연산 순서 변경을 막기 위해 반드시 모든 인자를 괄호로 감싸는 것이 원칙입니다.
- #define CUBE(x) SQUARE(x) * x ◦ PRINT(CUBE(a)); \(\rightarrow \) PRINT(SQUARE(a) * a) \(\rightarrow \) a * a * a ◦ \(a=5\)이므로 5 * 5 * 5 = \(125\)
- #define MAX(a, b) ((a) > (b) ? (a) : (b)) ◦ PRINT(MAX(a++, b)); \(\rightarrow \) ((a++) > (b) ? (a++) : (b)) ◦ \(a=5, b=3\)일 때, (5++) > 3이 참이므로 a는 6이 되고(첫 번째 a++), 결과로 6을 반환한 후, cout에 의해 출력됩니다. (하지만 대입문에 의해 a는 6이 된 상태) ◦ 실제 동작: (5 > 3 ? 5++ : 3) 형태가 되어 a는 6이 됩니다.
MAX(a++, b)에서 a가 최종적으로 7이 되는 이유는 매크로가 실행되면서 a++ 연산이 두 번 실행되기 때문입니다.이것을 매크로의 '이중 계산(Double Evaluation)' 문제라고 합니다. 과정을 단계별로 뜯어보면 이렇습니다.1. 매크로 치환 단계먼저 #define MAX(a, b) ((a) > (b) ? (a) : (b)) 정의에 따라 코드가 그대로 치환됩니다. • 원래 코드: MAX(a++, b) • 치환된 코드: ((a++) > (b) ? (a++) : (b))2. 실행 및 계산 단계 (a=5, b=3 일 때)
- 조건문 비교: (a++) > (b)를 먼저 계산합니다. ◦ 5 > 3이므로 결과는 **참(True)**입니다. ◦ 이때 후위 증가 연산(a++)에 의해 a는 6이 됩니다.
- 결과값 선택: 조건이 참이므로 ? 뒤에 있는 (a++) 부분이 실행됩니다. ◦ 현재 a는 6이므로, 이 매크로의 최종 결과값은 6이 됩니다. ◦ 동시에 후위 증가 연산(a++)이 한 번 더 실행되면서 a는 7이 됩니다.요약하자면 • 비교할 때 한 번: a가 5에서 6으로 증가 • 참인 값을 가져올 때 한 번: a가 6에서 7로 증가결국 MAX 매크로 한 번에 a++가 두 번 작동하여 최종 a값은 7이 되는 것입니다.이런 치명적인 부작용(Side Effect) 때문에 실무에서는 매크로 함수 대신 **인라인 함수(Inline Function)**나 **템플릿(Template)**을 사용하는 것을 권장합니다.
- PRINT(a); ◦ MAX 매크로에서 a++가 수행되어 (a)값은 6이 됩니다.
출력 결과 • SQUARE(a+1) = 11 • CUBE(a) = 125 • MAX(a++, b) = 6 (주의: 환경에 따라 a가 두 번 증가할 수 있으나, 일반적으로는 6 출력) • a = 7 (위의 MAX 매크로 내에서 a++가 두 번 발생하지 않더라도, 이 예제에서는 보통 a++로 인해 6, 이후 다시 7로 최종 증가됨)위의 분석과 가장 일치하는 것은 ④번입니다.
정답: ④ SQUARE(a+1) = 11 CUBE(a) = 125 MAX(a++, b) = 6 a = 7
9. 다음 C++ 코드가 여러 번 실행될 때, 가장 적절한 설명은?
#include <iostream>
#include <cstdlib>
using namespace std;
int main() {
// 시드값 설정
srand(12345);
// 5개의 난수 생성 및 출력
cout << "난수 5개: ";
for(int i = 0; i < 5; i++) {
cout << rand() % 100 << " ";
}
cout << endl;
return 0;
}
①프로그램을 실행할 때마다 다른 난수가 생성됨
②프로그램을 실행할 때마다 항상 같은 난수 5개가 동일한 순서로 출력됨
③첫 번째 실행에서만 난수가 생성되고, 그 이후 실행에서는 모두 0이 출력됨
④srand(12345)는 난수의 범위를 0부터 12345까지로 제한함
개념: srand(), rand()
풀이
- srand(12345); (시드 설정): srand 함수는 난수 생성기(rand)의 시작 지점(씨앗)을 설정합니다. 시드값이 고정되면(12345) rand() 함수가 생성하는 난수의 수열도 항상 동일합니다.
- rand() % 100: 0
32767 사이의 난수를 생성한 후 100으로 나눈 나머지, 즉 099 사이의 정수 5개를 출력합니다. - 결과: 프로그램을 1번 실행하든 100번 실행하든, srand에 동일한 숫자(12345)를 넣었기 때문에 항상 같은 난수 5개가 동일한 순서로 출력됩니다.
참고: 프로그램을 실행할 때마다 다른 난수를 얻으려면 srand(time(NULL));과 같이 현재 시간을 시드값으로 사용해야 합니다.
정답: ②프로그램을 실행할 때마다 항상 같은 난수 5개가 동일한 순서로 출력됨
10. 다음은 C++ 클래스의 오버로딩 예제이다. 출력 결과가 아래와 같이
출력되기 위해서 Ⓐ에 알맞은 코드는 무엇인가?
#include <iostream>
using namespace std;
class Calculator {
public:
int sum(int a, int b) { return a + b; }
double sum(double a, double b) { return a + b; }
};
int main() {
Calculator calc;
cout << calc.sum(3, 4) << " ";
cout << calc.sum( Ⓐ ) << endl; // 알맞은 코드를 선택
return 0;
}
출력결과
7 7.5
①3, 4 ②3.5, 4 ③3.5, 4.0 ④'3', 4.5
개념: 함수 오버로딩, 정수(int), 실수(float)
풀이:
- sum 함수는 int형 두 개를 받는 함수와 double형 두 개를 받는 함수로 오버로딩되어 있습니다.
- calc.sum(3, 4)는 int 버전을 호출하여 7을 출력합니다.
- 출력 결과 7.5가 나오기 위해서는 double sum(double a, double b) 함수가 호출되어야 하므로, Ⓐ에 들어갈 인자는 실수형(floating-point) 리터럴이어야 합니다.따라서 cout << calc.sum( 3.5, 4.0 ) << endl;을 작성하면 7.5가 출력됩니다.
정답: ③3.5, 4.0
11. 다음 중 옵저버(Observer)패턴에 대한 설명으로 가장 적절한 것은?
①객체의 상태가 변경될 때 의존 객체들에게 자동으로 통지하는 일대다 의존성을 정의한다. ②객체를 생성하기 위한 인터페이스를 정의하고, 어떤 클래스의 인스턴스를 생성할지는 서브클래스가 결정하도록 한다. ③기존 코드를 수정하지 않고 기능을 추가할 수 있게 해주는 구조적 패턴이다. ④알고리즘의 골격을 정의하고 일부 단계를 서브클래스에서 구현하도록 유도한다
개념: 디자인패턴
옵저버 패턴(Observer Pattern), 팩토리 메서드(Factory Method) 패턴, 데코레이터(Decorator) 패턴, 템플릿 메서드(Template Method) 패턴
풀이
① 객체의 상태가 변경될 때 의존 객체들에게 자동으로 통지하는 일대다 의존성을 정의한다. • **옵저버 패턴(Observer Pattern)**의 핵심 정의입니다. 한 주제(Subject) 객체의 상태가 바뀌면 이를 관찰(Observe)하고 있는 의존 객체(Observer)들에게 자동으로 알림(Notify)이 가고, 내용을 갱신하는 방식을 의미합니다
② 객체를 생성하기 위한 인터페이스를 정의하고, 어떤 클래스의 인스턴스를 생성할지는 서브클래스가 결정하도록 한다. • 이는 팩토리 메서드(Factory Method) 패턴에 대한 설명입니다.
③ 기존 코드를 수정하지 않고 기능을 추가할 수 있게 해주는 구조적 패턴이다. • 이는 데코레이터(Decorator) 패턴 등의 설명과 유사하며, 개방-폐쇄 원칙(OCP)을 따르는 구조를 말합니다.
④ 알고리즘의 골격을 정의하고 일부 단계를 서브클래스에서 구현하도록 유도한다. • 이는 템플릿 메서드(Template Method) 패턴에 대한 설명입니다
정답: ①객체의 상태가 변경될 때 의존 객체들에게 자동으로 통지하는 일대다 의존성을 정의한다.
12. 아래의 코드를 C++11로 컴파일을 할 때 에러가 발생하는 라인은?
1: #include <iostream>
2: #include <vector>
3:
4: int main()
5: {
6: auto count = 10L;
7: auto values = std::vector<int>{ 9, 10, 11 };
8: auto& first = values[0];
9: auto result;
10: if (count > first) result = " > ";
11: else result = " <= ";
12: std::cout << count << result << first;
13:
14: return 0;
15: }
① 6 ② 7 ③ 8 ④ 9
개념: auto 키워드
풀이:
C++11에서 auto 키워드를 사용하여 변수를 선언할 때는 반드시 **초기화(Initialization)**가 이루어져야 합니다. 컴파일러가 초기화 식을 바탕으로 auto 변수의 타입을 추론해야 하기 때문입니다. auto result;와 같이 초기값 없이 선언하면 컴파일러는 result의 타입을 알 수 없으므로 에러(error: declaration of 'auto result' has no initializer)를 발생시킵니다.
해결 방법: const char result = nullptr; // 또는 적절한 초기값 지정*
정답: ④ 9 | auto result;
13. 아래의 대화에서 (A)와 (B)에 들어갈 가장 적절한 용어의 조합은?
팀원A : 사용 중인 길찾기 검색의 CPU 부하가 너무 높은 것 같아요. CPU 부하를 (A) 할 방법이 없을까요? 팀원B : 길찾기 검색의 결과를 (B) 해두었다가, 동일한 시작점과 도착점에 대한 요청이 들어오면 검색을 다시 실행하지 않고 저장된 결과를 바로 사용하는 방법은 어떨까요?
①(A) 최적화(Optimization), (B) 캐싱(caching) ②(A) 자동화(Automation), (B) 캐싱(caching) ③(A) 최적화(Optimization), (B) 인덱싱(indexing) ④(A) 자동화(Automation), (B) 인덱싱(indexing)
개념:
풀이:
(A)에는 전체적인 성능 개선을 의미하는 '최적화', (B)에는 임시 저장 기술을 의미하는 '캐싱'이 들어가는 것이 가장 적절합니다. • (A) 최적화(Optimization): 시스템의 자원(CPU 등)을 효율적으로 사용하여 성능을 향상시키는 행위입니다. • (B) 캐싱(Caching): 자주 사용되는 데이터나 결과값을 빠른 메모리에 임시로 저장하여, 동일한 요청이 들어왔을 때 계산을 다시 하지 않고 결과를 즉시 제공하는 기술입니다
다른 용어 설명
**1. 자동화 (Automation)**사람이 수동으로 하던 반복적인 업무나 복잡한 프로세스를 소프트웨어나 기계가 스스로 수행하도록 만드는 것입니다. • 핵심 목적: 효율성 증대, 인적 오류(실수) 감소, 시간 절약. • 예시: 매일 아침 특정 사이트의 뉴스를 긁어오는 '크롤링', 정해진 시간에 서버 상태를 점검하는 '스케줄러' 등이 있습니다.
**2. 인덱싱 (Indexing)**방대한 양의 데이터 중에서 원하는 내용을 빠르게 찾을 수 있도록 별도의 **'색인(목차)'**을 만드는 과정입니다. • 핵심 목적: 데이터 검색 속도의 획기적인 향상. • 비유: 두꺼운 책의 맨 뒷장에 있는 '찾아보기'와 같습니다. 책장 전체를 한 장씩 넘기지 않아도, 인덱스에서 페이지 번호를 찾아 바로 원하는 정보로 건너뛸 수 있게 해줍니다. • 데이터베이스 예시: 수백만 개의 사용자 정보 중 '이름'으로 검색할 때, 인덱스가 걸려 있지 않으면 처음부터 끝까지 다 뒤져야 하지만 인덱싱이 되어 있으면 순식간에 찾아낼 수 있습니다.
정리하자면 이렇습니다: • 최적화: 전체적인 성능을 "더 좋게" 만드는 것 • 캐싱: 결과물을 "저장"해 두었다가 다시 쓰는 것 • 자동화: 사람 대신 "기계"가 돌아가게 하는 것 • 인덱싱: 정보를 빨리 찾으려고 "목차"를 만드는 것
정답: ①(A) 최적화(Optimization), (B) 캐싱(caching)
14. 일반적인 서버-클라이언트 게임 아키텍처에서 서버의 주요 역할이
라 볼 수 없는 것은?
①게임 정보 저장 등의 데이터베이스 관리 ②게임 세계의 최종 상태 결정 및 유지 ③게임 화면 렌더링 및 사용자 입력 처리 ④게임 불법 행위 감지 및 제재 시스템 운영
개념: 서버, 클라이언트
풀이:
일반적인 서버-클라이언트 구조(특히 온라인 게임)에서 렌더링(화면 그리기)과 사용자 입력 처리는 클라이언트(사용자의 PC, 모바일 기기)의 역할입니다. 서버는 게임의 로직을 처리하고 데이터의 정당성을 검증하는 역할을 합니다. • ① 데이터베이스 관리: 서버 역할 (맞음) • ② 최종 상태 결정 및 유지: 서버 역할 (권한 서버, 맞음) • ④ 불법 행위 감지 및 제재: 서버 역할 (핵 방지, 맞음)
정답: ③게임 화면 렌더링 및 사용자 입력 처리
15. 아래의 코드를 실행하면 출력되는 값으로 맞는 것은?
#include <iostream>
template <typename T, typename U>
void Func(T a, U b) { std::cout << "A"; }
template <typename T>
void Func(T a, T b) { std::cout << "B"; }
template <>
void Func<int>(int a, int b) { std::cout << "C"; }
template <>
void Func<double>(double a, double b) { std::cout << "D"; }
int main()
{
Func(1, 2);
Func(1.5, 2.5);
Func(1, 2.5);
return 0;
}
① AAA ② BBA ③ CDA ④ CDD
개념: 템플릿 (Template), 오버로딩
풀이:
코드 분석 및 템플릿 매칭
- template <typename T, typename U> void Func(T a, U b) (템플릿 A): 두 인자의 타입이 다를 수 있는 일반 템플릿입니다. 호출 시 "A"를 출력합니다.
- template void Func(T a, T b) (템플릿 B): 두 인자의 타입이 같아야 하는 템플릿입니다. 호출 시 "B"를 출력합니다.
- template <> void Func(int a, int b) (특수화 C): Func<int, int>에 대한 명시적 특수화입니다. 호출 시 "C"를 출력합니다.
- template <> void Func(double a, double b) (특수화 D): Func<double, double>에 대한 명시적 특수화입니다. 호출 시 "D"를 출력합니다.
메인 함수 호출 분석 • Func(1, 2);: 두 인자 모두 int입니다. ◦ 템플릿 B(T=int)와 특수화 C(Func)가 후보입니다. ◦ C++ 오버로딩 규칙에 따라 특수화된 버전(C)이 우선순위가 높습니다. -> C 출력 • Func(1.5, 2.5);: 두 인자 모두 double입니다. ◦ 템플릿 B(T=double)와 특수화 D(Func)가 후보입니다. ◦ 특수화된 버전(D)이 우선순위가 높습니다. -> D 출력 • Func(1, 2.5);: 인자가 int, double로 타입이 다릅니다. ◦ 템플릿 B는 T a, T b로 타입이 같아야 하므로 탈락합니다. ◦ 템플릿 A(T=int, U=double)가 인스턴스화되어 매칭됩니다. -> A 출력따라서, CDA 순서로 출력됩니다.
Func(1, 2) → int, int이므로 C
Func(1.5, 2.5) → double, double이므로 D
Func(1, 2.5) → int, double로 타입이 달라 템플릿 A가 선택되어 A
정답: ③ CDA
16. 아래의 코드를 실행하면 출력되는 값으로 맞는 것은?
#include <iostream>
#include <list>
#include <algorithm>
int main()
{
std::list<int> numbers = { 10, 20, 30, 40, 50 };
auto it = std::find(numbers.begin(), numbers.end(), 30);
numbers.insert(it, *it + 5);
for (auto it = numbers.rbegin(); it != numbers.rend(); ++it)
std::cout << *it << " ";
return 0;
}
①10 20 30 35 40 50 ②10 20 35 30 40 50 ③50 40 35 30 20 10 ④50 40 30 35 20 10
개념: 컨테이너, list, insert, rbegin, rend
풀이:
- 초기화: std::list numbers = { 10, 20, 30, 40, 50 };
- 검색: std::find를 통해 값 30을 가리키는 반복자 it를 찾습니다.
- 삽입: numbers.insert(it, *it + 5);에 의해 30 앞(it 위치)에 30 + 5인 35가 삽입됩니다. ◦ 리스트 상태: { 10, 20, 35, 30, 40, 50 }
- 역순 출력: rbegin()부터 rend()까지 역방향 반복자를 사용하여 출력합니다. ◦ 출력 순서: 50 40 30 35 20 10 [1]따라서, 올바른 출력값은 50 40 30 35 20 10 입니다.
정답: ④50 40 30 35 20 10
17. 온라인 멀티플레이어 게임에서 '클라이언트 사이드 예측 (Client-side Prediction)은 플레이어의 입력을 서버 응답을 기다리지 않고 로컬에서 즉시 처리하여 실행 결과를 예측하는 기술입니다. 이 기술을 구현하는 주된 이유는?
①서버의 처리 부담을 줄이기 위해 ②네트워크 지연(latency)으로 인한 사용자 경험 저하를 완화하기 위해 ③클라이언트 간 공정성을 보장하기 위해 ④서버 코드 복잡성을 줄이기 위해
개념: 서버, 클라이언트
풀이
**클라이언트 사이드 예측(Client-side Prediction)**은 멀티플레이어 게임에서 네트워크 지연 시간(Ping)으로 인해 플레이어의 입력과 화면 반응 사이에 발생하는 시간차를 줄이는 기술입니다. 서버의 응답을 기다리지 않고 로컬 클라이언트에서 즉시 결과를 화면에 보여줌으로써, 플레이어는 로컬 게임을 하는 듯한 **즉각적인 반응성(Responsiveness)**을 느끼게 되어 사용자 경험이 크게 향상됩니다.
- ① 서버의 처리 부담은 오히려 예측 및 보정 로직으로 인해 늘어날 수 있습니다.
- ③ 공정성은 주로 서버 권한 방식(Server Authoritative)을 통해 보장됩니다.
- ④ 클라이언트와 서버 양쪽에 예측 및 검증 코드를 작성해야 하므로 복잡성은 증가합니다.
정답: ②네트워크 지연(latency)으로 인한 사용자 경험 저하를 완화하기 위해
문제 18. 아래의 코드 성능을 가장 효과적으로 개선할 수 있는 방법은?
1: void ProcessPhysics(std::vector<RigidBody>& bodies)
2: {
3: for (int i = 0; i < bodies.size(); i++)
4: {
5: for (int j = 0; j < bodies.size(); j++)
6: {
7: if (i != j)
8: {
9: CheckCollision(bodies[i], bodies[j]);
10: }
11: }
12: }
13: }
14:
15: void CheckCollision(RigidBody& a, RigidBody& b)
16: {
17: //계산 비용이 높은 물리 연산 수행
18: }`
① 1라인의 매개변수인 bodies를 std::vector 대신 배열을 사용하여 메모리 접근 속도를 향상시킨다.
② 3라인과 5라인의 bodies.size()의 결과를 변수에 저장하여 루프마다 계산하지 않도록 한다.
③ 5라인의 j = 0 대신 모든 객체 쌍을 한 번씩만 검사하도록 내부 루프의 시작점을 j = i + 1로 변경한다.
④ 15라인의 매개변수 a와 b를 레퍼런스에서 포인터로 전달되도록 코드를 변경한다.
개념:
풀이:
• 기존 코드의 문제점: 현재 코드는 O(N^2)의 시간 복잡도를 가지며, (A)와 (B)의 충돌을 체크한 후, 나중에 (B)와 (A)의 충돌을 중복으로 체크합니다 (i ≠ j) 조건만으로는 중복이 제거되지 않음). • 개선 방법 (③): 내부 루프를 j = i + 1로 시작하면 (A, B) 쌍을 한번 체크했을 때, 나중에 (B, A) 쌍을 다시 체크하지 않게 됩니다. 이는 연산 횟수를 거의 절반 ((approx N^2 / 2))으로 줄이는 가장 효과적인 알고리즘적 개선입니다. • 다른 보기들: ◦ ①: vector와 배열의 메모리 접근 속도 차이는 최신 컴파일러에서는 거의 없으며, std::vector는 연속된 메모리를 사용하여 캐시 효율이 좋습니다. ◦ ②: bodies.size()는 컴파일러가 최적화하여 밖으로 빼는 경우가 많으며, (N^2) 연산에 비해 미미한 성능 향상입니다. ◦ ④: 레퍼런스(&)와 포인터(*)는 기계어 수준에서 거의 동일하게 처리되므로 성능 차이가 없습니다.따라서 계산 비용이 높은 CheckCollision을 절반으로 줄이는 ③번이 가장 효과적입니다.
정답: ③ 5라인의 j = 0 대신 모든 객체 쌍을 한 번씩만 검사하도록 내부 루프의 시작점을 j = i + 1로 변경한다.
문제 19. 아래의 C++11 코드를 실행하면 출력되는 값으로 맞는 것은?
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
std::vector<int> numbers = { 5, 2, 8, 1, 9 };
std::sort(numbers.begin(), numbers.end(),
[](int a, int b)
{
return a > b;
}
);
for (int num : numbers) std::cout << num << " ";
return 0;
}`
① 5 2 8 1 9
② 9 1 8 2 5
③ 1 2 5 8 9
④ 9 8 5 2 1
개념: vector, sort, 람다
풀이
- 초기 벡터: std::vector numbers = { 5, 2, 8, 1, 9 };
- 정렬 방식: std::sort 함수에 [](int a, int b) { return a > b; }라는 람다 식을 비교 함수(comp)로 전달했습니다. ◦ C++의 std::sort는 비교 함수가 true를 반환하면 첫 번째 인자(a)를 두 번째 인자(b)보다 앞순서에 배치합니다. ◦ a > b가 true인 경우, 즉 더 큰 숫자를 앞쪽에 배치하므로 **내림차순(큰 수 -> 작은 수)**으로 정렬됩니다.
- 정렬 결과: { 9, 8, 5, 2, 1 }
- 출력: 반복문을 통해 정렬된 요소를 공백으로 구분하여 출력합니다.
따라서 9 8 5 2 1이 출력됩니다.
정답: ④ 9 8 5 2 1
문제 20. C++에서 friend 선언에 관한 설명 중 옳은 것은?
① 클래스 A가 클래스 B를 friend로 선언하면, 두 클래스는 서로의 private 멤버와 protected 멤버에 상호 접근이 가능하다.
② 기본 클래스에서 선언한 friend 관계는 모든 파생 클래스에 자동으로 적용된다.
③ 클래스 A에서 friend로 선언된 함수나 클래스는 클래스 A의 private 멤버와 protected 멤버에 접근할 수 있다.
④ friend 선언은 클래스의 public 영역에서만 할 수 있으며, private이나 protected 영역에서는 불가능하다.
개념: friend
참고: https://learn.microsoft.com/ko-kr/cpp/cpp/friend-cpp?view=msvc-170 경우에 따라 클래스의 멤버가 아닌 함수 또는 별도의 클래스에 있는 모든 멤버에게 멤버 수준의 액세스 권한을 부여하는 것이 유용할 수 있습니다. 이러한 무료 함수 및 클래스는 friend 키워드로 표시된 friends라고 합니다. 클래스 구현자만 이 클래스의 friend를 선언할 수 있습니다. 함수 또는 클래스는 자신을 클래스의 friend로 선언할 수 없습니다. 클래스 정의에서 friend 키워드 및 비멤버 함수 또는 기타 클래스의 이름을 사용하여 클래스의 전용 멤버 및 보호된 멤버에 대한 액세스 권한을 부여합니다. 템플릿 정의에서 형식 매개 변수는 **friend**로 선언할 수 있습니다.
friend 선언은 private 멤버의 접근을 허용하는 선언이다.
- A 클래스가 B 클래스를 대상으로 friend 선언을 하면, B클래스는 A클래스의 private 멤버에 직접 접근이 가능하다.
- 단, A 클래스도 B 클래스의 private 멤버에 직접 접근이 가능하려면, B 클래스가 A 클래스를 대상으로 friend 선언을 해줘야 한다.
- 특정 멤버함수를 대상으로도 friend 선언이 가능
풀이:
- **① 클래스 A가 클래스 B를 friend로 선언하면, 두 클래스는 서로의 private 멤버와 protected 멤버에 상호 접근이 가능하다. (거짓)**friend 관계는 비대칭적입니다. A가 B를 friend로 선언하면 B는 A의 private 멤버에 접근할 수 있지만, A가 B의 private 멤버에 접근할 수는 없습니다.
- **② 기본 클래스에서 선언한 friend 관계는 모든 파생 클래스에 자동으로 적용된다. (거짓)**friend 관계는 상속되지 않습니다.
- **③ 클래스 A에서 friend로 선언된 함수나 클래스는 클래스 A의 private 멤버와 protected 멤버에 접근할 수 있다. (참)**friend로 선언된 대상은 해당 클래스의 캡슐화 규칙(private, protected)을 우회하여 멤버에 접근할 수 있습니다.
- **④ friend 선언은 클래스의 public 영역에서만 할 수 있으며, private이나 protected 영역에서는 불가능하다. (거짓)**friend 선언은 클래스 내부의 public, private, protected 영역 어디에서나 위치할 수 있으며, 위치에 따른 차이는 없습니다.
정답: ③ 클래스 A에서 friend로 선언된 함수나 클래스는 클래스 A의 private 멤버와 protected 멤버에 접근할 수 있다.
문제 21. 표준 C언어 컴파일러로 printf()함수에 의해서 출력되는 결과값으로 올바른 것은?
#include <stdio.h>
int main(void)
{
char a=128;
printf("%d", a );
return 0;
}
① 0
② -1
③ -128
④ 128
개념: 데이터 타입의 범위, 오버플로우
풀이:
- 데이터 타입의 범위: 대부분의 표준 C언어 컴파일러 환경(8비트 char)에서 char 타입은 signed char로 취급되며, 범위는 -128부터 127까지입니다.
- 오버플로우(Overflow): char에 128을 저장하려고 하면, 127보다 크기 때문에 char 범위 내에서 가장 작은 수인 -128로 오버플로우(데이터 랩 어라운드)가 발생합니다.printf
- 출력: %d는 int형으로 출력하라는 서식 지정자이며, char 타입의 -128이 int 타입으로 형변환되어(정수 승격) 10진수 -128로 출력됩니다
정답: ③ -128
문제 22. 아래에 설명하는 디스크 스케줄링에 맞는 기법을 고르시오.
가장 간단한 스케줄링 기법으로 요청 대기 큐에 먼저 들어온 순서대로 스케줄링 한다
① FCFS (First Come First Served)
② SSTF (Shortest Seek Time First)
③ SCAN (=Elevator Algorithm)
④ SLTF (Shortest-Latency-Time-First)
개념:
풀이: • 개념: FCFS(First-Come-First-Served)는 디스크 I/O 요청이 들어온 순서대로 큐(Queue)에 넣고, 그 순서대로 헤드를 이동하며 서비스를 제공하는 가장 단순한 디스크 스케줄링 기법입니다. • 풀이: ◦ 요청 큐에 먼저 들어온 순서대로 처리하므로 공평성(Fairness)이 보장됩니다. ◦ 탐색 시간 최적화를 위한 재정렬이 없기 때문에, 요청들이 디스크 전체에 무작위로 분포되어 있을 경우 헤드 이동 거리가 길어져 비효율적일 수 있습니다. ◦ 문제에서 "요청 대기 큐에 먼저 들어온 순서대로 스케줄링 한다"고 명시했으므로 FCFS가 정답입니다. [1,
참고: • ② SSTF (Shortest Seek Time First): 현재 헤드 위치에서 가장 가까운 트랙 요청을 먼저 처리. • ③ SCAN (=Elevator Algorithm): 헤드가 한쪽 끝에서 반대쪽 끝으로 이동하며 길에 있는 요청을 처리. • ④ SLTF (Shortest-Latency-Time-First): 회전 지연 시간(Latency)이 가장 짧은 요청을 먼저 처리
정답: ① FCFS (First Come First Served)
문제 23. C언어의 전역 스코프에 사용된 전처리기에 대한 예제 코드다. 다음 중 컴파일시에 오류가 발생하는 코드는 무엇인가?
① #ifndef MYCLASS_H_
#define MYCLASS_H_
//..중략..
#endif // MYCLASS_H_
② #define YOUR_NAME "KOCCA"
③ #define FUNC
{
printf("FUNC Name is KOCCA");
}
④ #define MAX(a,b) ((a) > (b) ? (a) : (b))
개념: 매크로
풀이:
#define 전처리기는 컴파일 전에 텍스트를 단순 치환(텍스트 대체)하는 역할을 합니다.
- ③ #define FUNC { printf("FUNC Name is KOCCA"); }: 매크로 내에 문장 블록({ ... })을 사용하여 세미콜론(;)을 포함하고 있습니다. 이 매크로가 코드 내에서 사용될 때, 전처리기가 단순 치환을 수행하면 문법에 맞지 않는 형태(예: if(...) { printf("..."); }; )가 되거나, 세미콜론이 두 번 들어가는 등의 문제가 발생하여 컴파일러가 인식하지 못하는 구문 오류가 발생할 수 있습니다. 매크로로 여러 줄의 코드를 정의하려면 \\ 문자를 사용하여 한 줄로 연결하거나 do { ... } while(0) 형태를 사용해야 합니다.
나머지 코드 설명
- ① #ifndef...#endif: 헤더 가드(Header Guard)의 일반적인 형태로 올바른 표현입니다.
- ② #define YOUR_NAME "KOCCA": 매크로 상수를 정의하는 올바른 표현입니다.
- ④ #define MAX(a,b)...: 매크로 함수를 정의하는 올바른 표현입니다.
③번 매크로를 문법 오류 없이 안전하게 사용하려면:
백슬래시(\\**)**를 사용해 여러 줄을 하나로 연결하거나, 실행 시 논리적 결함이 없도록 do { ... } while(0) 구조로 감싸는 것이 정석입니다.
1. 백슬래시(\\)로 연결하기
C언어 전처리기는 줄 바꿈을 매크로의 끝으로 인식합니다. 여러 줄을 쓰려면 줄 끝에 \\를 붙여 "다음 줄과 이어진다"는 것을 알려줘야 합니다.
#define FUNC \\
{ \\
printf("FUNC Name is KOCCA"); \\
}
2. do-while(0) 패턴 사용 (가장 권장되는 방법)
단순히 { } 블록만 쓰면 if-else 문 안에서 사용할 때 세미콜론(;) 문제로 컴파일 에러가 날 수 있습니다. 이를 방지하기 위해 매크로 함수는 보통 아래와 같이 정의합니다.
#define FUNC \\
do { \\
printf("FUNC Name is KOCCA"); \\
} while(0)
수정 포인트:
- \\ (Line Continuation): 여러 줄에 걸쳐 정의할 때 필수입니다.
- do { ... } while(0): 매크로 뒤에 세미콜론(FUNC();)을 붙여도 문법적으로 완벽한 하나의 문장이 되도록 만들어줍니다.
이 매크로를 정의할 때 함수 본문 뒤에 세미콜론(;)을 직접 넣지 않도록 주의하세요.
정답: 3번
문제 24. 아래의 정적 라이브러리(Static Library)에 대한 설명 중 틀린 것을 고르시오.
① 프로그램 빌드 시에 라이브러리가 제공하는 코드를 실행 파일에 넣는 방식의 라이브러리를 의미한다.
② 컴파일의 링킹 단계에서 실행 파일에 결합 된다.
③ 실행 파일에 다 들어가기 때문에 실행 시 라이브러리가 필요 없다.
④ 같은 정적 라이브러리를 사용한 여러 프로그램을 실행할 경우 메모리를 절약한다.
개념: 정적 라이브러리(Static Library), 동적 라이브러리 (Dynamic Library)
풀이:
정적 라이브러리(Static Library)는 빌드 시 실행 파일에 라이브러리 코드가 포함되기 때문에, 같은 라이브러리를 사용하는 프로그램을 여러 개 실행하면 메모리(RAM)에 해당 라이브러리 코드가 프로그램 수만큼 중복해서 올라갑니다. 따라서 메모리를 절약하는 것이 아니라 오히려 낭비하게 됩니다.
①, ②, ③은 정적 라이브러리에 대한 올바른 설명입니다. (링킹 단계에서 포함되며, 실행 시 별도 파일 필요 없음)
참고: 메모리를 절약하는 방식은 프로그램 실행 시 필요한 부분만 메모리에 올리는 동적 라이브러리(Dynamic Library, .so/.dll) 방식입니다.
**동적 라이브러리(Dynamic Library)**는 프로그램 빌드 시 라이브러리 코드를 실행 파일에 넣지 않고, 프로그램이 실행될 때(Runtime) 또는 실행 중 필요할 때 라이브러리를 불러와 연결하는 방식입니다.
윈도우에서는 .dll (Dynamic Link Library), 리눅스에서는 .so (Shared Object) 확장자를 사용합니다.
주요 특징 및 장단점
- 메모리 및 디스크 공간 절약: 실행 파일에는 실제 코드 대신 라이브러리의 위치 정보만 포함되므로 파일 크기가 작습니다. 또한 여러 프로그램이 동일한 동적 라이브러리를 사용하면, 운영체제가 메모리에 해당 라이브러리를 하나만 올려서 공유하므로 메모리 효율이 매우 높습니다.
- 유지보수 및 업데이트 용이: 라이브러리 내 함수가 수정되더라도 함수의 규격(인수, 반환값 등)이 바뀌지 않았다면, 프로그램을 다시 컴파일하지 않고 라이브러리 파일만 교체하면 바로 업데이트가 적용됩니다.
- 실행 시 라이브러리 필수: 프로그램 실행 시 해당 라이브러리 파일이 시스템 내에 존재해야 합니다. 만약 파일이 없거나 경로가 잘못되면 프로그램이 실행되지 않습니다.
- 상대적으로 느린 로딩 속도: 실행 시점에 라이브러리를 찾고 주소를 해석하는 과정이 필요하므로, 모든 코드가 포함된 정적 라이브러리에 비해 초기 실행 속도나 함수 호출 시 오버헤드가 미세하게 발생할 수 있습니다
정답: ④ 같은 정적 라이브러리를 사용한 여러 프로그램을 실행할 경우 메모리를 절약한다.
문제 25. (ㄱ)에 들어가는 적절한 것을 다음 중에서 고르시오.
(ㄱ)는 HTML 문서와 같은 리소스들을 가져올 수 있도록 해주는 프로토콜이다. (ㄱ)는 웹에서 이루어지는 모든 데이터 교환의 기초이며, 클라이언트-서버 프로토콜이기도 합니다. 모바일 게임 서버 중 수집형 장르의 게임 서버는 (ㄱ)을 사용하기도 한다.
① TCP
② UDP
③ HTTP
④ ICMP
개념: 네트워크 프로토콜 계층 구조에
풀이:
- (ㄱ) HTTP (HyperText Transfer Protocol): 웹 브라우저(클라이언트)와 웹 서버 사이에서 HTML 문서, 이미지 등 리소스를 가져오기 위해 사용하는 클라이언트-서버 프로토콜입니다.
- 웹의 가장 기초적인 데이터 교환 프로토콜이며, REST API 등을 통해 모바일 게임 등에서 서버와 데이터를 주고받을 때도 널리 사용됩니다.
- ① TCP, ② UDP는 전송 계층 프로토콜, ④ ICMP는 네트워크 계층에서 오류 보고에 사용되는 프로토콜입니다
네트워크의 핵심 프로토콜인 TCP, UDP, ICMP는 각각 데이터의 신뢰성, 속도, 진단을 담당하며 역할이 뚜렷하게 나뉩니다.
1. TCP (Transmission Control Protocol)신뢰성과 정확한 전달을 최우선으로 하는 프로토콜입니다.
- 특징: '3-Way Handshake' 과정을 통해 먼저 연결을 맺고 데이터를 보냅니다. 데이터가 유실되면 재전송을 요청하며, 수신측의 속도에 맞춰 전송량을 조절(흐름 제어)합니다.
- 사용 예: 웹 서핑(HTTP), 이메일 전송, 파일 전송(FTP) 등 데이터 손실이 치명적인 서비스.
**2. UDP (User Datagram Protocol)**신뢰성보다는 빠른 전송 속도와 효율성을 중시하는 프로토콜입니다.
- 특징: 수신 확인이나 재전송 과정이 없어 TCP보다 훨씬 빠르지만, 데이터가 중간에 사라지거나 순서가 바뀔 수 있습니다.
- 사용 예: 실시간 영상 스트리밍, 온라인 게임, VoIP(인터넷 전화)처럼 약간의 데이터 유실보다 지연 시간 최소화가 중요한 서비스.
3. ICMP (Internet Control Message Protocol) 데이터 전송보다는 네트워크의 상태 진단과 오류 보고를 위해 존재합니다.
- 특징: 특정 대상에 데이터를 보내는 것이 아니라, "상대방 컴퓨터가 켜져 있는지", "경로가 막혀 있지는 않은지" 등을 확인하는 메시지를 주고받습니다.
- 사용 예: ping 명령어로 연결 상태 확인, traceroute로 데이터 이동 경로 추적.
정답: ③ HTTP
문제 정답 | 2025 게임프로그래밍전문가 필기시험 답안_B형

추천글
게임국가기술자격검정 게임프로그래밍전문가 [한국콘텐츠진흥원] | 종합
게임국가기술자격검정 게임프로그래밍전문가 [한국콘텐츠진흥원] | 종합
About this Certification 홈페이지: https://www.kgq.or.kr/service/main.do검정안내: https://www.kgq.or.kr/service/info/cert.do국가기술자격증 (유효기간 없음)Since 2003시험일정 : https://www.kgq.or.kr/service/rcpt/sc_list.do원서접
devcol.tistory.com
필기시험 관련 기초 개념 공부 | 게임국가기술자격검정 게임프로그래밍전문가 [한국콘텐츠진흥원]
- Part 1 : 2025 게임 프로그래밍 전문가 국가기술 자격검정 필기시험 B 풀이 |Part1. 게임 프로그래밍 방법론: 1~25 | 게임국가기술자격검정 게임프로그래밍전문가 [한국콘텐츠진흥원]
- Part 2 : Part2. 게임 알고리즘과 설계: 1~25 | 2025 게임 프로그래밍 전문가 국가기술 자격검정 필기시험 B 풀이 | 게임국가기술자격검정 게임프로그래밍전문가 [한국콘텐츠진흥원]
- Part 3: Part3. 게임 알고리즘과 설계: 1~25 | 2025 게임 프로그래밍 전문가 국가기술 자격검정 필기시험 B 풀이 | 게임국가기술자격검정 게임프로그래밍전문가 [한국콘텐츠진흥원]
