스터디를 진행하며 실시한 모의 면접을 정리한 내용입니다.
겹치는 내용을 고려하지 않고 면접을 실시하기 때문에 일자 별로 겹치는 내용이 많을 수 있습니다.

1. (C++) void*에 대해 설명하라.

더보기

C++에서 자료형이란, 메모리 영역에 저장된 바이너리 데이터를 어떻게 해석하는 지를 규정하는 것입니다. 그렇기 떄문에 일반적인 데이터는 자료형이 반드시 정해져 있어야 하지만, 해당 데이터를 직접적으로 사용하지 않고 저장된 바이너리 데이터 혹은 저장된 주소값을 참조하는 것만 필요한 경우엔 void*를 활용할 수 있습니다.

 

예를 들어 쉐이더에 상수버퍼 데이터를 전달하는 경우, 상수버퍼의 자료형은 GPU 내에서 결정되기 때문에 CPU에선 자료형과 무관한 바이너리 데이터만 전달하면 됩니다. 이러한 특징으로 인해 GPU에 전달할 데이터를 void* 타입으로 캐스팅하여 관리한다면 편하고 일관성있는 데이터 관리 구조를 구축할 수 있게 됩니다.

2. 동시성과 병렬성이란 무엇인가?

더보기

여러 작업이 물리적으로 동시에 처리되는 것을 병렬성이라고 합니다. 멀티코어 CPU의 경우 각 코어당 하나의 작업을 처리할 수 있기 때문에 물리적으로 여러 작업이 동시에 실행될 수 있습니다.

 

반면, 싱글코어의 경우 동시에 하나의 작업만 수행할 수 있기 때문에 여러 작업이 물리적으로 동시에 실행될 수는 없습니다. 이러한 한계를 극복하고 여러 프로세스가 동시에 실행되는 듯한 경험을 사용자에게 제공하기 위해 운영체제는 CPU 스케줄링 기법을 활용하여 여러 프로세스를 초고속으로 번갈아 실행하게 됩니다. 이처럼 실제로는 동시에 실행되고 있지 않지만 동시에 실행되는 것처럼 보이는 것을 동시성이라고 합니다.

3. (C++) 빌드과정에 대해 설명하라.

더보기

가장 먼저 전처리기 과정이 수행됩니다. 전처리기 단계에선 프로그램 실행에 불필요한 주석, 공백 등을 제거하고 매크로에 의해 정의된 변수, 함수 등을 모두 실제 값으로 치환하는 과정을 거칩니다.

또한, include된 헤더파일의 내용을 모두 cpp파일의 상단에 삽입하여 파일을 통합하는 과정을 거칩니다. 

 

이 과정이 끝난 뒤에는 컴파일 단계를 수행하게 됩니다. 컴파일 단계에선 C++로 작성된 코드를 저수준의 어셈블리어로 변환하는 과정을 거칩니다. 코드를 변환하는 과정에서 문법상의 오류를 검출하는 과정을 동시에 거치게 됩니다. 프로그램이 정상적으로 실행될 수 없을 정도의 오류를 발견한다면 그 즉시 빌드를 중단하게 됩니다.

 

컴파일 단계가 끝난 뒤엔 어셈블리 단계를 거치게 됩니다. 이 단계에선 어셈블리어로 변환된 코드를 기계어로 변환하는 과정을 거치게 됩니다. 코드를 기계어로 변환한 뒤엔 object 파일을 생성하게 됩니다.

 

어셈블리 단계를 마친 뒤엔 링킹 과정을 거치게 됩니다. 이 단계에선 여러 개의 오브젝트 파일과 정적 라이브러리를 결합하여 실행할 수 있는 하나의 exe 파일을 생성하게 됩니다.

 

이 4개의 과정이 모두 끝나면 빌드가 종료되며 실행할 수 있는 프로그램이 생성됩니다.

4. 동기와 비동기에 대해 설명하라.

더보기

동기란 이전의 작업이 종료되어야 다음 작업이 수행될 수 있는 것을 의미합니다. 반면, 비동기는 이전 작업의 종료 여부와 관계 없이 다음 작업이 수행될 수 있는 경우를 의미합니다.

 

예를 들어, 단일 스레드 환경에선 이전의 작업이 완료되어야 다음 작업이 수행될 수 있기 때문에 동기적이라고 할 수 있지만 멀티 스레드 환경에서 메인 스레드의 작업과 독립적으로 수행되고 있는 작업이 있다면 이를 비동기적이라고 할 수 있습니다.

5. (C++) \n과 std::endl의 차이에 대해 설명하라.

더보기

\n은 단순히 입력 버퍼에 \n이라는 줄바꿈 문자를 추가하는 것입니다. 문자가 출력될 때, \n을 기점으로 줄바꿈을 수행하여 출력하게 됩니다.

 

std::endl은 기존의 메세지를 모두 출력하고 줄바꿈을 수행한 뒤 입력 버퍼를 비우는 작업도 함께 실행하게 됩니다.

6. (C++) 리터럴 문자열을 char [] 에 저장할 때와 char* 에 저장할 때 차이는?

더보기

char [] 과 같은 배열 형태로 리터럴 문자열을 저장하게 되면, 스택영역에 존재하는 배열 내부에 바이트 단위로 문자열을 나누어 저장하게 됩니다. 

 

반면, char*로 저장하게 되면 리터럴 문자열은 데이터 영역에 저장되고 char*는 리터럴 문자열이 저장된 메모리 주소값을 저장하게 됩니다.

 

데이터 영역에 저장된 문자열을 여러 곳에서 재활용될 수 있는 문자열이기 때문에 함부로 수정하게 되면 위험한 상황이 발생할 수 있습니다. C++은 이러한 위험성을 방지하기 위해 char* 타입으로 리터럴 문자열을 저장하는 것을 허용하지 않고 있으며 반드시 const char* 타입으로 저장하도록 강제하고 있습니다.

7. (C#) 코루틴이란?

더보기

일반적인 함수는 return 문을 수행해야 지역을 탈출하지만, 코루틴 함수는 중간에 함수 실행을 중단한 뒤 나중에 재개하여 사용할 수 있습니다. 입력 대기 시간 등에 코루틴 함수를 활용하여 다른 작업을 수행하도록 설계한다면 프로세스의 성능을 높일 수 있습니다.

8. (C++) 배열과 포인터의 차이에 대해 설명하라.

더보기

포인터란, 특정 메모리 영역의 주소를 저장하는 변수입니다. 저장된 주소값을 통해 메모리 영역에 간접적으로 접근할 수 있게 됩니다. 이를 활용하면 불필요한 데이터의 복사를 방지할 수 있으며, 변수가 선언된 지역 외부에서도 접근할 수 있게 됩니다. 

 

배열은 일렬로 나열된 데이터의 가장 앞 부분을 가리키는 일종의 포인터 변수입니다. 하지만, 가리키는 주소값이 변경될 경우 배열의 원소를 더이상 참조할 수 없기 때문에 가리키는 대상을 변경할 수 없다는 제약이 존재합니다.

9. (C++) SSO란?

더보기

SSO란 std::string의 동적할당으로 인한 오버헤드를 조금이라도 줄이기 위해, 길이가 짧은 문자열은 힙영역이 아닌 스택영역에 저장하도록 하는 최적화 기법입니다.

10. (C++) union이란?

더보기

하나의 데이터를 다양항 방식으로 활용하기 위해 사용하는 문법입니다. union을 활용하면 동일한 4바이트의 데이터라고 하더라도 4개의 char 타입의 원소로 이루어진 배열로 사용할 수도 있고, int 타입 변수로도 사용할 수도 있게 됩니다.

11. (C++) Lvalue와 Rvalue에 대해 설명하라.

더보기

L-Value란 이름이 존재하며, 지역을 벗어나거나 메모리를 해제하기 전까지 소멸되지 않는 값입니다. 반면, R-Value는 이름이 존재하지 않으며 표현식이 끝난 이후 사라지는 임시의 값입니다.

12. (C#) Nullable이란?

더보기

null을 사용할 수 없는 int, char 등의 자료형에 null을 사용할 수 있도록 도와주는 객체입니다. 

13. (C++) operator new 란?

더보기

C++의 동적할당 함수인 new의 경우 메모리를 할당하고 생성자를 호출한 뒤 메모리 주소를 타입캐스팅하여 반환하는 3개의 과정으로 실행됩니다. operator new는 이 중에서 메모리를 할당하는 역할을 담당하는 함수입니다. C언어의 malloc처럼 크기에 맞는 메모리를 할당하여 이를 반환해줍니다. operator new는 의도적으로 재정의하는 것도 가능하다는 특징이 있습니다.

14. 람다 함수란?

더보기

람다 함수란, 이름이 없는 함수를 의미합니다. 일반적인 함수는 미리 선언, 정의를 해놓은 뒤 이를 재활용하는 방식으로 사용됩니다. 반면, 람다 함수는 필요할 때 즉석에서 정의하여 사용할 수 있으며, 함수의 사용이 끝나면 메모리에서 소멸하게 됩니다. 

15. 메시란?

더보기

오브젝트가 화면에 그려지는 위치를 결정짓기 위한 데이터 덩어리입니다. 여러 개의 버텍스로 구성되어 있으며 각 버텍스는 위치,  법선, 색상 등의 정보를 포함할 수 있습니다.

16. is-A 와 has-A에 대해 설명하라.

더보기

두 가지 모두 객체간의 관계를 표현하는 방식입니다. is-A란 대입 연산이 가능한 경우를 의미합니다. 상속 관계에 있는 두 객체에서 하위 객체는 상위 객체에 대입 연산이 가능하기 때문에 is-A 관계라고 할 수 있습니다.

 

반면, has-A 관계는 한 객체가 다른 객체를 소유하고 있는 경우를 의미합니다. 컴포넌트 관계처럼 한 객체가 다른 객체를 멤버 변수로 소유하여 그 기능을 활용할 수 있는 경우 has-A 관계라고 합니다.

스터디를 진행하며 실시한 모의 면접을 정리한 내용입니다.
겹치는 내용을 고려하지 않고 면접을 실시하기 때문에 일자 별로 겹치는 내용이 많을 수 있습니다.

1. (C#) reflection이란?

더보기

런타임에 객체의 정보를 파악하기 위해 사용되는 기술입니다. 리플렉션을 활용하면 런타임에 객체, 필드, 메서드 등의 정보를 얻을 수 있습니다. 

2. (C#) 델리게이트란?

더보기

델리게이트란 메서드를 더욱 유연하게 사용할 수 있도록 도와주는 문법입니다. 델리게이트에 함수를 바인딩하여 사용하면 call back을 편하고 안전하게 구현할 수 있으며, 동시에 호출되어야 하는 여러 메서드를 하나로 묶어 관리할 수 있게 됩니다.

3. (유니티) update, fixedupdate, lateupdate에 대해 설명하라.

더보기

Update와 LateUpdate는 매 프레임마다 호출되는 함수입니다. 오브젝트가 매 프레임마다 수행해야 하는 작업이 있다면, Update혹은 LateUpdate 내부에 코드를 작성하여 처리할 수 있습니다. Update와 lateUpdate 모두 매 프레임마다 호출되지만, LateUpdate 함수는 모든 오브젝트의 Update 함수가 호출된 이후에 호출됩니다. 모든 객체에 대해 Update 가 이루어진 이후에 추가적으로 처리해야 할 작업이 있다면, LateUpdate에서 처리할 수 있습니다.

 

FixedUpdate는 프레임마다 호출되는 함수가 아니라 일정 주기마다 호출되는 함수입니다. Update와 LateUpdate는 호출 간격이 들쑥날쑥한 반면, FixedUpdate는 정해진 주기마다 호출되기 때문에 항상 일정한 호출 간격을 보장받습니다. 이러한 특징으로 인해 물리 연산을 주로 FixedUpdate에서 처리합니다.

4. (유니티) prefab 이란?

더보기

씬에서 사용될 게임 오브젝트를 미리 정의하여 파일의 형태로 보관하는 것을 프리팹이라고 합니다. 해당 게임 오브젝트가 보유할 컴포넌트, 트랜스폼 정보 등을 정의하여 파일의 형태로 저장한 뒤, 실제 씬에 오브젝트가 생성될 때, 해당 파일의 값을 참조하여 오브젝트를 생성하게 됩니다. 원본이 변하지 않고 항상 동일한 상태를 유지하기 때문에 생성되는 오브젝트들의 통일성을 보장할 수 있습니다.

5. (C#) 박싱과 언박싱에 대해 설명하라. 이를 대체할 수 있는 문법이 있는가?

더보기

boxing은 데이터를 object타입 혹은 참조 형식으로 변환하는 것을 의미하며, unboxing은 변환된 것을 원래의 상태로 되돌리는 것을 의미합니다. 하나의 클래스가 다양한 자료형에 대응하기 위해선, 모든 자료형이 공통으로 상속받고 있는 object 클래스를 이용해야 합니다. 이를 위해, 데이터를 object 클래스의 참조형으로 변환하여 통일성있게 저장하고, 실제로 사용할 땐 다시 원본의 상태로 되돌려 값을 사용하게 됩니다. 하지만, boxing과 unboxing은 그 횟수가 많아질수록 성능에 큰 부담을 주게 됩니다. 이를 완화하기 위해 generic이라는 문법을 사용할 수 있습니다. boxing, unboxing은 런타임에 실행되는 반면, generic은 대부분의 연산이 컴파일 타임에 처리되기 때문에 성능 부분에서 큰 향상을 기대할 수 있습니다.

6. (C#) LINQ에 대해 설명하라.

더보기

C#언어에 포함되어 있는 쿼리 기능입니다. LINQ는 다량의 데이터를 편하고 안전하게 사용할 수 있도록 도와줍니다. LINQ는 C#에서 제공하는 컬렉션에만 사용가능한 것이 아니라, 프로세스 외부의 데이터베이스나 XML 등에도 편리하게 사용할 수 있습니다.

7. (C#) ref와 out 키워드에 대해 설명하라.

더보기

ref와 out은 모두 참조 전달에 사용되는 키워드입니다. ref의 경우 특정 변수를 참조하도록 하는 키워드입니다. ref로 선언된 변수는 선언과 동시에 가리키는 대상이 정해져야 하며, 가리키는 대상은 반드시 초기화가 되어있어야 합니다.

 

 out 키워드는 참조 전달 중에서도 함수 내부에서 값을 저장해주는 역할을 위해 사용되는 키워드입니다. out의 경우 가리키는 대상이 초기화가 되어 있지 않아도 되지만, 함수 내부에서는 반드시 값을 저장해주어야 합니다.

 

ref를 이용해 out을 모두 대체할 수는 있지만, 구분하여 사용함으로써 변수가 어떻게 사용되는 지를 더욱 명확히 할 수 있습니다.   

8. (C#) interface에 대해 설명하라.

더보기

여러 클래스 간에 공통의 인터페이스를 정의하기 위해 사용되는 문법입니다. 추상 클래스와 유사하지만, 모든 함수가 abstract로 선언되어야 하며 멤버 변수 및 부모 클래스를 가질 수 없다는 차이점이 있습니다. 이러한 차이는 인터페이스가 다중 상속을 통해 사용된다는 특징으로 인해 발생합니다.

 

다중 상속은 다이아몬드 문제 등 여러 문제를 야기할 수 있기 때문에 C#에선 다중상속을 막아놓았지만, 인터페이스는 다중 상속을 통해 구현되는 기능이기 때문에 interface로 선언된 클래스에만 다중 상속이 허락되어 있습니다. 상술했던 추상 클래스와 인터페이스의 차이점은 다중상속으로 인한 문제점을 방지하기 위해 언어 차원에서 제약을 걸어둔 것이라고 할 수 있습니다.

9. (C#) colider와 rigidbody에 대해 설명하라.

더보기

Colider는 충돌을 탐지하기 위해 사용되는 컴포넌트입니다. 각 오브젝트 간의 충돌 검사를 위해선 Colider 컴포넌트가 필수적으로 필요합니다.

 

RigidBody는 대상에 다양한 물리를 적용하기 위해 사용되는 컴포넌트입니다. 중력, 마찰력 등의 물리법칙을 간편하게 오브젝트에 적용할 수 있게 됩니다. 

 

만약, 주먹에 맞은 대상이 후방으로 밀려나는 효과를 구현하고 싶다면, Colider를 사용해 주먹과 대상의 충돌을 검사한 뒤, RigidBody를 통해 일정 힘을 가함으로써 대상을 밀려나도록 할 수 있습니다.

10. (유니티) 레이캐스팅이란 무엇인가? 사용한다면 어디에 사용할 수 있는지 예시가 있는가?

더보기

레이캐스팅이란,  특정 지점에서 광선을 발사하여 경로에 존재하는 오브젝트를 검출하는 충돌 검사 기법입니다. 3D 공간에 있는 물체를 클릭하는 기능을 구현하고 싶을 때, 2D 공간에 투영되어 있는 마우스 좌표를 3D 월드 좌표로 변환한 뒤 레이캐스팅을 사용하여 경로에 있는 물체를 탐지하여 상호작용이 발생하도록 구현할 수 있습니다. 뿐만 아니라, 총을 발사하는 것을 레이캐스팅을 사용해 구현할 수도 있으며 전방의 벽을 탐지하기 위해서도 사용할 수 있습니다. 

11. (유니티) 모노비헤이비어란?

더보기

객체와 유니티 엔진을 결합하기 위해 상속해야 하는 클래스입니다. 모노비헤이비어 클래스를 상속받음으로써 클래스와 에디터를 연동할 수 있으며, 객체의 생명주기가 유니티 엔진에 의해 관리되도록 할 수 있습니다. 또한, 클래스 내에서 유니티 엔진의 프레임워크를 사용할 수 있게 되면서 편리하고 유연한 개발이 가능해집니다.

12. (C#) 코루틴이란 무엇인가?

더보기

함수를 중지하거나 재개하는 기능을 제공해주는 문법입니다. 코루틴에 의해 중지된 함수를 재개하는 것은 일반적인 함수 호출에 비해 빠르기 때문에, 코루틴 함수 내부에 반복문으로 기능을 정의한 후 이를 중지하고 재개하는 방식으로 활용한다면 성능의 향상을 기대할 수 있게 됩니다. 또한, 입력, 응답 등을 기다리는 대기 시간이 발생할 때 코루틴 함수를 재개하여 다른 작업을 처리하도록 한다면 작업 사이의 비는 시간을 최소화하여 성능을 극대화할 수 있게 됩니다.

13. (C#) const와 readonly 키워드에 대해 설명하라. 

더보기

두 키워드 모두 값의 수정을 막는 키워드입니다. const의 경우 대상을 아예 상수화하기 때문에 컴파일 단계에서 값이 정해져야 합니다. 그렇기 때문에 const로 선언된 변수는 선언과 동시에 초기화가 이루어져야 합니다. 런타임에 생성자가 호출된 이후 값이 정해지는 커스텀 클래스의 경우 const 키워드를 사용할 수 없으며, 멤버 변수로 선언된 const 변수는 모든 인스턴스가 같은 값을 공유하게 된다는 특징이 있습니다.

 

반면, readonly 키워드의 경우 힙영역에 동적으로 메모리를 할당하여 값을 저장하며, 생성자에 한해 런타임에 값이 변경되는 것이 허락됩니다. 이러한 특징으로 인해, 모든 자료형에 대해 readonly 키워드를 사용할 수 있으며 인스턴스 별로 다른 값을 보유할 수 있게 됩니다. 하지만, 런타임에 힙영역을 참조하는 이유로 const 변수에 비해 낮은 성능을 보유하게 된다는 단점이 있습니다. 

14. (C#) event란 무엇인가?

더보기

델리게이트에 제약이 추가된 특수한 형태입니다. 발행 구독 패턴을 사용해 특정 상황에 실행되어야 하는 여러 함수들을 관리할 때 유용하게 사용할 수 있습니다.

 

event는 일반적인 델리게이트와 달리 대입 연산이 불가능하고 증감연산만 가능하며, event를 소유한 객체 외부에서 바인딩된 함수를 호출할 수 없다는 특징이 있습니다.

15. (유니티) mono와 il2cpp에 대해 설명하라.

더보기

유니티에서 제공하는 스크립팅 백엔드입니다. 모노의 경우 런타임에 코드를 기계어로 변환하기 때문에 컴파일 타임이 il2cpp에 비해 빠르지만, 런타임 퍼포먼스가 il2cpp에 비해 저조하다는 단점이 있습니다.

 

반면, il2cpp는 빌드 과정에서 기계어 번역을 마치기 때문에 컴파일 타임이 느리다는 단점이 있지만, 모노에 비해 런타임 퍼포먼스가 우수하다는 장점이 있습니다.

 

프로젝트에 있어 효율성, 개발 속도 등에서 이점을 챙기고자 한다면 모노를 사용하는 것이 유리하며 고성능 퍼포먼스를 노리고자 한다면 il2cpp를 사용하는 것이 좋습니다.

16. (C#) 기본 자료형(int, float)등에 대해 null을 사용할 수 있는가?

더보기

일반적으로는 null을 대입할 수 없지만, Nullable 객체를 활용하면 null을 대입할 수 있게 됩니다. 

스터디를 진행하며 실시한 모의 면접을 정리한 내용입니다.
겹치는 내용을 고려하지 않고 면접을 실시하기 때문에 일자 별로 겹치는 내용이 많을 수 있습니다.

 

1. 얕은 복사와 깊은 복사에 대해 설명하라.

더보기

얕은 복사는 값을 그대로 복사하는 것입니다. 얕은 복사의 경우 연산이 매우 가볍지만 포인터 변수가 가지고 있는 주소값을 그대로 복사하기 때문에 댕글링 포인터 등의 문제가 발생할 수 있다는 문제점이 있습니다. 이러한 위험성을 해결하기 위해 새로운 메모리 영역을 할당하여 포인터 변수가 가리키고 있는 메모리 주소에 저장된 값을 복사하는 것을 깊은 복사라고 합니다.

2. (C#) is와 as 연산자에 대해 설명하라.

더보기

클래스의 캐스팅 성공 여부를 확인하는 연산자입니다. is연산자는 특정 클래스가 다른 클래스로 캐스팅이 가능한지 여부를 파악하여 true, false로 가능 여부를 반환해줍니다.

 

as연산자는 클래스의 캐스팅 여부를 확인하는 동시에 캐스팅이 가능하다면 캐스팅을 함께 진행해줍니다. 즉, as 연산자는 캐스팅 성공시엔 캐스팅된 대상을 반환하며 실패시엔 null을 반환합니다. \

3. (C#) sortedset과 dictionary에 대해 설명하라.  

더보기

두 클래스 모두 C#의 컬렉션에 포함된 클래스입니다. sortedset의 경우 이진 트리 기반의 자료구조입니다. 데이터를 key로만 저장하며 원소가 삽입되거나 삭제될 때마다 정렬 작업을 실행합니다. 

 

반면, dictionary는 해시테이블 기반의 자료구조입니다. 데이터를 해싱된 key와 value를 쌍으로 저장하며, 원소가 삽입되거나 삭제되어도 정렬 작업을 수행하지 않습니다.

 

이러한 특성들로 인해, dictionary가 sortedset에 비해 속도 측면에선 우수한 성능을 보이지만 dictionary는 데이터가 너무 적을 때엔 메모리 낭비가 심할 수 있다는 단점이 있습니다. 

4. (C#) List 자료구조란?

더보기

C#의 컬렉션에 포함된 자료구조이며, generic을 기반으로 반들어진 클래스입니다. List는 일종의 동적 배열이기 때문에, 데이터가 메모리에 연속적으로 위치하고 있으며 배열의 크기가 가변적이라는 특성이 있습니다.

5. (C#) boxing, unboxing이란? 

더보기

박싱이란, 값 형식을 참조 형식으로 변환하는 것을 의미하며 언박싱이란 박싱된 데이터를 원래의 형식으로 돌려놓는 것을 의미합니다. C#의 모든 자료형은 object를 상속받고 있기 때문에, object의 참조 형식을 활용하면 다양한 자료형을 통일성있게 사용이 가능합니다. 하지만, 박싱과 언박싱은 커다란 오버헤드를 유발하는 작업이기 때문에 최적화 측면에선 지양하는 것이 좋습니다.

6. 멀티스레드 프로그래밍의 주의사항은?

더보기

공유되는 자원이 훼손되지 않도록 주의하여 설계해야 합니다. 하나의 프로세스에 속한 여러 스레드는 스택 메모리를 제외한 모든 메모리를 공유하게 됩니다. 여러 스레드가 같은 메모리 영역에 대해 읽고 쓰는 작업을 동시다발적으로 수행하게 된다면, 해당 메모리 영역엔 실제 프로그래머의 의도와 다른 값이 저장될 수 있습니다. 이를 방지하기 위해선 아토믹, 뮤텍스, 세마포어 등을 활용할 수 있습니다. 

7. 직렬화와 역직렬화란? 

더보기

직렬화는 현재 메인 메모리에 저장되어 있는 데이터를 바이트 스트림으로 변환하는 것을 의미하며, 역직렬화는 바이트 스트림으로 저장되어 있는 데이터를 읽어 메인 메모리에 저장하는 것을 의미합니다. 주로 게임의 세이브 데이터나 네트워크 송수신에 사용됩니다.

8. TCP와 UDP에 대해 설명하라.

더보기

TCP와 UDP는 모두 네트워크 통신 프로토콜입니다. TCP는 연결 지향성 프로토콜로, 통신하는 두 대상이 1:1로 연결된 상태로 통신을 하게 됩니다. 3way handshake, 4way handshake를 통해 안전하게 두 대상을 연결하며, 송수신 과정에서 데이터가 훼손되지 않도록 흐름 제어, 혼잡 제어 등의 기법을 추가적으로 활용합니다. 이로 인해, 매우 높은 데이터의 신뢰성을 기대할 수 있지만, 통신 속도가 상대적으로 느리다는 단점이 있습니다.

 

UDP는 비연결 지향성 프로토콜로, 통신하는 대상이 서로 연결되지 않은 상태로 통신하게 됩니다. 이로 인해, 패킷이 수신되는 경로가 일정하지 않아 수신 순서와 송신 순서가 달라질 수 있어 데이터가 훼손될 가능성이 있습니다. 하지만 UDP는 통신에 있어 추가적인 데이터 검증, 보호 절차를 거치지 않기 때문에 매우 빠른 통신 속도를 보인다는 장점이 있습니다.

 

9.  (C++) int 자료형은 몇바이트인가?

더보기

c++ 표준에선 자료형의 크기가 구현체에 따라 달라질 수 있음을 명시하고 있습니다. 즉, int 자료형의 크기는 일정하지 않으며 운영체제 등의 환경에 따라 크기가 달라질 수 있습니다. 그렇기 때문에, 자료형을 사용할 때엔 __int32, __int64등 그 크기가 함께 명시되어 있는 자료형을 사용하는 것이 좋습니다. 

10. (C#) partial 이란?

더보기

하나의 클래스를 여러 파일에서 나누어 정의할 수 있도록 해주는 키워드 입니다. 아주 많은 메서드를 가진 클래스를 하나의 파일에서 모두 정의하는 경우 가독성이 매우 떨어질 수 있기 때문에, 클래스를 여러 조각으로 나누어 정의하고자 할 때 사용하는 키워드입니다.

11. (C#) base 키워드에 대해 설명하라.

더보기

바로 위의 부모 클래스에 접근할 수 있도록 도와주는 키워드 입니다. base 키워드를 사용하면 부모 클래스의 메서드, 프로퍼티 등을 자식 클래스에서 직접적으로 사용할 수 있게 됩니다.

12. (C++) std::function 이란?

더보기

C++에서 함수 포인터, 함수 객체, 람다 함수 등의 callable을 편하고 안전하게 사용할 수 있도록 제공해주는 템플릿 클래스입니다. 일반적인 C스타일의 함수 포인터에 비해 가독성이 좋고 사용성이 매우 편리하고 안전하게 사용할 수 있는 클래스이며, std::bind 함수와 함께 사용하면 파라미터와 함수를 묶어 더욱 편리하고 유연하게 사용할 수 있게 됩니다

13. (C++) NULL과 nullptr의 차이에 대해 설명하라.

더보기

NULL과 nullptr은 모두 값이나 가리키는 대상이 없음을 표현할 때 사용하는 키워드입니다. NULL은 0을 매크로를 사용해 정의한 것으로 정수 0과 완전히 동일합니다. 그렇기 때문에 여러 파라미터에 대해 오버로딩된 함수를 호출하는 경우, 포인터 타입이 아닌 정수값을 파라미터로 받는 함수를 호출할 가능성이 있습니다.

 

이러한 문제를 해결하기 위해 C++에선 포인터임을 명시하는 nullptr이라는 키워드를 제공해줍니다. nullptr은 정수 0이 아닌 포인터 타입으로 간주되기 때문에 포인터 변수를 파라미터로 받는 함수를 정확하게 호출할 수 있게 됩니다.

 

즉, NULL은 값형이지만 nullptr은 참조형이라는 것이 차이라고 할 수 있습니다.

14. (C++ ) 빌드 과정에 대해 설명하라.

더보기

C++ 코드의 빌드는 전처리기, 컴파일, 어셈블리, 링커 이렇게 크게 4개의 단계로 이루어집니다. 전처리기 단계에선 주석을 제거하고 불필요한 공백을 제거한 뒤, 코드에 사용된 매크로를 모두 실제 사용되는 값으로 치환하는 과정을 거칩니다. 또한 포함된 헤더파일의 코드를 모두 복사하여 하나의 파일에 붙여넣어 헤더파일과 C++파일을 하나로 묶게 됩니다.

 

이후에는 컴파일 단계가 실행이 됩니다. 컴파일 단계에선 작성된 코드의 유효성을 검사하여 발생할 수 있는 오류를 사전에 잡아내는 역할을 합니다. 발견된 오류가 없다면 작성된 C++ 코드를 저수준의 어셈블리어로 변환하는 과정을 거치게 됩니다.

 

이후, 어셈블리 단계에선 어셈블리어로 변환된 코드를 실제 하드웨어가 이해할 수 있는 기계어로 변환하여 obj파일을 생성하게 됩니다.

 

마지막으로 실행되는 링커 단계에선 여러 개의 obj 파일과 정적 라이브러리를 하나로 묶어 최종적으로 실행될 수 있는 exe파일을 만들게 됩니다.

15. (C++) string_view란?

더보기

문자열의 불필요한 복사를 줄이기 위해 사용할 수 있는 클래스 입니다. 함수의 파라미터로 std::string을 사용할 경우, 참조형을 사용하더라도 상황에 따라 복사 및 동적 할당이 발생할 수 있습니다. 하지만, string_view는 내부에 const char* 타입의 변수와 문자열의 사이즈 만을 보유하고 있기 때문에 추가적인 동적 할당이 발생하지 않고, 복사 또한 매우 가벼운 연산으로 수행될 수 있습니다. 하지만, 읽기 전용 클래스인 만큼 string에 비해 문자열을 유연하게 사용하는 것이 불가능하므로 적재적소에 사용하는 것이 중요합니다.

16. (C++) this call 이란?

더보기

C++ 함수 호출 규약 중 하나로 멤버 함수를 호출할 때 사용되는 방식입니다. 멤버 함수는 호출될 때, 항상 호출자의 주소를 알고 있어야 하기 때문에, 멤버 함수는 컴파일 과정에서 파라미터의 가장 앞에 클래스의 포인터 형이 추가되고, 이 곳에 호출자의 주소가 대입되는 과정으로 실행됩니다. 함수 내부의 멤버함수 또한, 첫 번째 인자로 받은 포인터를 사용해 호출하는 방식으로 이루어집니다. 이러한 멤버 함수의 호출 방식을 this call이라고 합니다.

17. 프로세스란?

더보기

프로그램이 실행되어 메인 메모리에 위치하게 되면 이를 프로세스라고 부릅니다. 운영체제에서 독립적인 메모리를 할당하는 단위입니다. 

18. (C++) map과 unordered_map의 차이를 설명하라.

더보기

map과 unordered_map은 모두 key와 value를 쌍으로 저장하는 자료구조입니다. map은 완전 이진 트리를 기반으로 설계되어 있으며, 원소가 삽입되거나 삭제될 때마다 정렬을 하며 트리의 균형을 유지합니다. 

 

반면, unordered_map은 해시테이블 기반으로 설계되어 있으며, 원소가 삽입되거나 삭제되더라도 정렬을 수행하지 않습니다.

 

이러한 차이들로 인해, unordered_map을 사용하게 되면 일반적으로 map에 비해 높은 성능을 기대할 수 있지만 커스텀 클래스를 사용할 경우엔 해시 함수를 직접 구현해야 한다는 불편함이 있고, 데이터가 적을 때엔 메모리의 낭비가 심할 수 있다는 단점이 있습니다.

19. (C#) const 와 readonly의 차이에 대해 설명하라.

더보기

const는 컴파일 타임에 값이 상수로 치환되기 때문에, 선언과 동시에 초기화가 되어야 하며 데이터가 스택 영역에 저장됩니다. 반면, readonly는 생성자에서 값을 초기화 할 수 있으며 데이터가 힙 영역에 동적으로 저장됩니다.

 

const는 컴파일 타임에 상수화가 이루어지고 stack 영역에 저장되기 때문에 readonly로 선언된 변수에 비해 성능 부분에서 이점이 있지만, 기본 자료형에 대해서만 사용이 가능하고 값이 수정될 때마다 새로 컴파일을 해야 한다는 불편함이 있습니다.

 

반면 readonly는 런타임에 초기화가 이루어지고 데이터가 힙 메모리에 위치하기 떄문에 성능 상에선 다소 불리함이 있을 수 있지만, 커스텀 클래스에도 사용할 수 있으며 값을 수정하여도 새로 컴파일하지 않는다는 장점이 있습니다.

20.  다형성이란?

더보기

하나의 대상이 다양하게 표현될 수 있는 성질을 의미합니다. 상속관계의 클래스를 업캐스팅, 다운캐스팅 하는 것이 다형성의 대표적인 예시입니다.

21. (C#) sealed 키워드에 대해 설명하라.

더보기

sealed 키워드는 클래스의 파생을 제한하는 키워드입니다. sealed 키워드가 포함되어 정의된 클래스는 다른 클래스의 부모 클래스가 될 수 없으므로 항상 말단에 위치한 클래스임을 보장받습니다.

22. (C++) std::vector에서 at과 []의 차이를 설명하라.

더보기

[]으로 원소에 접근하는 경우, 대상에 빠르게 접근할 수 있지만 범위를 초과한 인덱스를 사용할 경우 예외를 방출하지 않고 프로세스를 종료하기 때문에 안정성이 낮고 디버깅에 불리하다는 단점이 있습니다.

 

반면, at을 사용하는 경우 범위를 초과한 인덱스를 사용할 경우 예외를 방출하기 때문에 디버깅 측면에선 유리하지만 []를 사용해 접근하는 것에 비해 느릴 수 있다는 단점이 있습니다.

23. 컨텍스트 스위칭이란?

더보기

CPU에서 실행중인 프로세스, 스레드가 다른 프로세스, 스레드로 교체되는 것을 의미합니다. 잦은 컨텍스트 스위칭은 성능 저하의 원인이 될 수 있으므로 프로그램을 설계할 때엔 컨텍스트 스위칭을 최소화하는 방향으로 설계해야 합니다.

24. 인터럽트란?

더보기

CPU가 작업하고 있는 것을 잠시 중단하고 먼저 처리해야 할 작업을 수행하는 것을 의미합니다. 주로 실행중인 작업에서 예외가 발생하여 이를 처리해야 할 경우에 인터럽트가 발생합니다.

25. (C++) malloc/free와 new/delete의 차이를 설명하라.

더보기

malloc의 경우엔 메모리만 할당해주고 추가적인 작업을 수행하지 않지만, new는 메모리 할당 뿐만 아니라 메모리 영역에 생성자 호출하여 값을 초기화 하는 작업과 메모리 영역의 포인터를 자료형에 맞게 타입 캐스팅하는 작업까지 이루어집니다.

 

이러한 과정의 차이로 인해, malloc는 반환형이 void*이며 생성자를 호출할 수 없지만 new의 경우 반환형이 자료형에 맞는 포인터 타입이며 생성자를 호출할 수 있습니다. 하지만, malloc은 자료형에 영향을 받지 않는 만큼 메모리 크기를 자유롭게 할당할 수 있지만 new의 경우엔 자료형이 차지하는 메모리 크기의 배수만큼만 할당할 수 있습니다.

26. 람다함수란?

더보기

람다함수란 이름이 없는 함수를 의미합니다. 일반적인 함수는 미리 선언과 정의를 해놓으면 이를 재사용하는 방식으로 이루어지지만 람다 함수는 필요할 때마다 즉석으로 정의하여 사용하는 방식으로 이루어집니다. 

 

람다함수는 스택 메모리에 저장되기 때문에 지역을 벗어나면 메모리에서 사라지지만 일반적인 함수는 프로세스가 끝날 때까지 코드 영역에 위치하기 때문에, 재사용성이 낮은 함수의 경우 람다함수를 사용하는 것이 메모리 측면에선 이점이 있을 수 있습니다.

 

또한, 필요할 때마다 그 자리에 정의하면 되기 때문에 사용성이 좋다는 장점이 있지만, 무분별하게 사용하게 되면 가독성을 심하게 저하할 수 있다는 단점이 있습니다. 

27. 마크 앤 스윕이란?

더보기

가비지 컬렉터 알고리즘의 한 종류입니다. 트리 구조로 연결되어 있는 오브젝트를 순회하여 참조 가능한 오브젝트에 표시를 한 뒤, 다시 모든 메모리를 훑어 표시가 되어 있지 않은 오브젝트를 제거하는 방식으로 이루어집니다. 

28. 배칭과 인스턴싱이란?

더보기

두 기법 모두 드로우 콜을 줄이기 위해 사용할 수 있는 기법입니다. 배칭은 같은 머티리얼을 공유하는 여러 매쉬를 하나로 결합하여 한 번의 드로우 콜로 렌더링을 해결하는 기법입니다.

 

인스턴싱이란 지오메트리 셰이더를 사용하여 동일한 지오메트리를 사용하는 여러 객체를 병렬적으로 렌더링하는 기법입니다. 전쟁 시뮬레이션 게임에서 수많은 병사를 렌더링할 때 인스턴싱 기법을 사용하면 병사의 수 만큼 드로우 콜이 발생하는 것이 아니라 1번의 드로우 콜로 렌더링을 해결할 수 있게 됩니다.

29. 메모리 단편화란?

더보기

메모리가 효율적으로 사용되지 못하는 상황을 의미합니다. 메모리 단편화는 내부 단편화와 외부 단편화로 분류됩니다.

 

내부 단편화란, 필요한 메모리보다 더 많은 메모리를 할당하여 메모리가 낭비되는 상황을 의미합니다. 외부 단편화란, 메모리의 할당과 해제가 반복되면서 할당된 메모리들 사이에 작은 틈이 발생하는 것을 의미합니다. 이러한 경우 실제 메인 메모리에 물리적 공간이 충분함에도 불구하고 연속적으로 존재하지 않기 때문에 메모리를 할당할 수 없는 상황이 발생할 수 있습니다.

30. (C#) generic 이란?

더보기

어떠한 클래스나 메서드 등을 선언할 때, 다양한 자료형에 대응하기 위해선 일반적으로 자료형에 따라 여러 개를 선언하여야 합니다. 이러한 불편함을 해결하기 위해 C#에서 지원해주는 키워드가 generic입니다. generic을 사용하게 되면 하나의 클래스나 메서드가 여러 자료형에 대해 대응할 수 있게 되며 코드의 재사용성이 매우 높아집니다.

스터디를 진행하며 실시한 모의 면접을 정리한 내용입니다.
겹치는 내용을 고려하지 않고 면접을 실시하기 때문에 일자 별로 겹치는 내용이 많을 수 있습니다.

1. 메모리 풀링이란?

더보기

런타임에 객체를 계속 생성하고 파괴하는 것이 오버헤드를 유발할 뿐만 아니라 외부 단편화의 원인이 될 수 있기 때문에 이를 방지하기 위해 미리 메모리를 크게 할당해놓고 사용하는 기법을 메모리 풀링이라고 합니다. 

2. 외부단편화란?

더보기

메모리의 할당과 해제가 반복되면 할당된 메모리 사이에 틈이 발생하게 됩니다. 이러한 틈이 많아지면 실제 메모리에 공간이 충분함에도 불구하고 메모리를 할당하지 못하거나 비효율적으로 할당해야 하는 상황이 발생할 수 있습니다. 이를 외부단편화라고 합니다.

3. 가상 메모리란?

더보기

HDD, SDD등의 보조 기억장치의 도움을 받아 메인 메모리의 물리적 용량보다 더 큰 범위의 용량을 사용하는 기법을 가상 메모리라고 합니다.  

4. 사용해본 경험이 있는 디자인 패턴있는가?

더보기

싱글톤 패턴을 사용해보았습니다. 플레이어의 스탯 혹은 보유한 아이템 정보 등을 보관하기 위해선 레벨과 무관하게 존재하는 클래스가 필요했고, 이를 전역 클래스로 구현하였습니다. 하지만, 이 클래스의 인스턴스가 여러개 생성되면 데이터가 안정적으로 보관되지 못할 가능성이 있기 때문에 하나의 인스턴스만 존재함을 보장해야했습니다. 이를 위해 싱글톤 패턴을 활용하였습니다.

5. (C++) 캐스팅 종류는 무엇이 있는가?

더보기

static_cast, dynamic_cast, reinterpret_cast, const_cast가 있습니다. static_cast는 컴파일 타임에 타입 캐스팅이 이루어지기 때문에 성능에 큰 영향을 주지 않는다는 장점이 있지만, 타입 캐스팅의 성공 여부를 반환하지 않아 안전하지 않다는 단점이 있습니다. 반면, dynamic_cast는 런타임에 타입 캐스팅이 이루어집니다. 타입 캐스팅이 실패시 nullptr을 반환하기 때문에 안전하게 사용할 수 있다는 장점이 있지만 성능에 영향을 줄 수 있다는 단점이 있습니다. 

reinterpret_cast는 가장 넓은 범위의 캐스팅을 지원하는 키워드입니다. static_cast는 값을 유지하기 위해 자료형에 따라 비트에 저장된 값을 변경하지만 reinterpret_cast는 비트에 저장된 값을 유지하는 방식의 캐스팅입니다. 다양한 형태의 타입 캐스팅을 지원하지만 자료형에 따라 값을 해석하는 것이 달라지기 때문에 안전하지 못하다는 단점이 있습니다. const_cast는 포인터 변수의 상수성을 일시적으로 제거해주는 타입 캐스팅입니다. 상수화 되어있는 포인터 변수가 다른 대상을 가리키게 하고 싶을 때 사용할 수 있습니다.

6. (C#) ref키워드에 대해 설명하라.

더보기

대상을 참조 타입으로 사용하기 위한 키워드입니다. C#에서 객체는 모두 참조 타입으로 사용되지만 구조체와 string을 제외한 기본 자료형 등은 기본적으로 참조 타입이 아니기 때문에 ref 한정자를 통해 참조 타입으로 사용할 수 있습니다.   

7. (C#) class와 struct의 차이는?

더보기

class는 new를 통해서만 생성할 수 있기 때문에 참조 형식으로만 사용할 수 있습니다. 반면, struct는 new 키워드 없이도 생성할 수 있어 값 타입으로 사용할 수 있습니다. struct는 일반적으로 stack영역에 위치하기 때문에 heap 영역에 위치하는 class보다 성능적인 부분에서 우위에 있지만, class와 달리 상속이 불가능하다는 단점이 있습니다. 

8. 동시성과 병렬성이란?

더보기

여러 작업이 동시에 실행되는 것처럼 보이지만 물리적으로는 동시에 실행되지 않는 것을 동시성이라 하며, 물리적으로 동시에 실행되고 있는 것을 병렬성이라고 합니다.

9. 유저영역과 커널영역이란?

더보기

운영체제에서 사용자가 마음껏 접근할 수 있는 영역을 유저영역이라 하며, 함부로 접근할 수 없도록 제한되어 있는 영역을 커널 영역이라고 합니다. 운영체제는 메모리 관리, 하드웨어 상호작용 등 저수준의 작업을 처리하는 역할을 합니다. 사용자가 이를 함부로 수정하게 되면 기기에 큰 문제를 발생시킬 수 있기 때문에 일부 영역에 대해선 접근할 수 없도록 막아두고 있으며, 이 영역을 커널 영역이라고 합니다. 반면, 문제가 발생하더라도 쉽게 해결할 수 있거나 국소적인 문제만 발생할 수 있는 영역에 대해선 자유로운 접근을 허용하고 있으며 이를 유저 영역이라고 합니다.

11. 외부단편화를 해결하는 방법은 무엇이 있나?

더보기

대표적으로 메모리 풀링 기법이 있습니다. 외부 단편화는 잦은 메모리의 할당과 해제로 인해 발생하기 때문에, 미리 메모리 블록을 할당해놓은 뒤 이를 재사용하는 메모리 풀링 기법을 활용하면 외부 단편화 문제를 최소화할 수 있습니다.

12. 네임스페이스란?

더보기

클래스, 구조체 등을 분류하기 위해 사용하는 문법입니다. 네임스페이스 안에 존재하는 클래스, 구조체 등은 네임스페이스를 통해 사용해야 하기 때문에 역할이나 사용처 등을 쉽게 알아볼 수 있다는 장점이 있습니다. 하지만, 매번 네임 스페이스를 통해 사용하는 것이 다소 불편하기 때문에 using 문법을 활용하여 이를 생략할 수도 있습니다. 

13. (C++) 가상 소멸자를 사용하는 이유는?

더보기

자식 클래스의 소멸자가 호출되는 것을 보장하기 위해 사용합니다. 특정 클래스가 부모 클래스로 업캐스팅되어있는 상태에서 소멸하게 되면, 자식 함수의 소멸자가 호출되지 않을 수 있습니다. 소멸자에서 메모리 해제 등의 중요한 작업이 이루어지는 경우 문제가 발생할 수 있기 때문에 소멸자를 가상화하여 반드시 호출되도록 할 수 있습니다.

14. 캐시 메모리란?

더보기

CPU와 메인 메모리의 작업 처리 속도 차이로 인해, CPU에서 메인 메모리에 접근하는 경우 CPU에 병목 현상이 발생할 수 있습니다. 현대의 컴퓨터는 이러한 병목현상을 완화하기 위해, CPU가 메인 메모리에 접근했을 때 일정량의 데이터를 캐시 메모리로 복사한 뒤 CPU가 캐시 메모리의 데이터를 먼저 탐색하도록 설계되어 있습니다. 캐시 메모리는 메인 메모리에 비해 매우 빠른 작업 처리 속도를 보유하고 있기 때문에 높은 성능 향상을 기대할 수 있습니다.

15. 캐시 적중률이란?

더보기

원하는 데이터가 캐시 메모리에서 발견될 확률을 캐시 적중률이라고 합니다. 캐시 메모리에 원하는 데이터가 없을 경우 CPU는 메인 메모리에 접근하여 데이터를 다시 캐시로 옮겨오는 작업을 실행하게 됩니다. 원하는 데이터를 캐시 메모리에서 많이 발견할 수 있다면 CPU가 메인 메모리에 접근하는 횟수가 적어지기 때문에 캐시 적중률이 높을수록 높은 성능을 기대할 수 있습니다.

16. (C++) 접근 제한 지정자란 무엇인가? 종류는 무엇이 있는가?

더보기

접근 제한 지정자란, 외부의 접근을 허용하는 정도를 설정하는 키워드 입니다. 접근 제한 지정자는 public, protected, private 총 3개의 종류가 있습니다. public는 모든 곳에서 접근을 허용하는 키워드이며, protected는 상속 관계에서만 접근을 허용하는 키워드입니다. private는 어느 곳에서도 접근을 허용하지 않는 키워드입니다.

17. 초기화 리스트와 리터럴 초기화의 차이는?

더보기

리터럴 초기화는 변수가 생성되는 동시에 값을 초기화 하지만, 초기화 리스트는 생성자가 호출될 때 초기화를 실행하기 때문에 리터럴 초기화보다 늦은 시기에 실행되게 됩니다.

18. TCP와 UDP의 차이를 설명하라.

더보기

TCP는 연결 지향형 통신 프로토콜입니다. 소켓을 이용해 서로를 1:1로 연결하여 통신하기 때문에 안전하게 통신할 수 있습니다. 하지만, 안정성을 위해 패킷 송수신에 부가적인 작업을 많이 하는 만큼 반응성은 다소 느리다는 단점이 있습니다.

 

UDP는 비연결 지향형 통신입니다. 서로 연결되어 있지 않고 데이터 검증, 보호 등의 작업을 수행하지 않기 때문에 데이터의 신뢰성이 TCP에 비해 낮다는 단점이 있지만 매우 빠르게 통신할 수 있다는 장점이 있습니다.

 

이러한 특징으로 인해 TCP는 파일의 전송 등 데이터의 신뢰성이 중요한 작업에 자주 이용되며, UDP는 실시간 게임이나 동영상 스트리밍처럼 데이터의 신뢰성보다 속도에 중점을 둬야하는 경우 자주 사용하게 됩니다.

19. (C#) abstract와 interface에 대해 설명하라.

더보기

abstract는 대상을 추상화하는 키워드입니다. abstract 키워드에 의해 추상화 된 대상은 독립적으로 존재할 수 없게 되므로 자식 클래스에서 대상을 구체화해야만 정상적으로 사용이 가능합니다.

interface는 추상화된 객체에 몇가지 제약을 더해 외부에서 편하고 안전하고 기능을 사용할 수 있도록 도와주는 키워드입니다. interface는 C#에서 유일하게 다중상속이 허락된 객체이지만, 다중상속의 위험성을 방지하기 위해 부모 클래스는 가지는 것이 불가능하고 멤버 변수를 가질 수 없습니다. 또한, 내부에 선언된 메서드는 모두 자식 클래스에서 재정의되어야만 합니다. 

20. 객체지향의 4가지 특징에 대해 설명하라.

더보기

객체 지향의 4가지 특징은 상속, 캡슐화, 추상화, 다형성입니다. 상속은 한 클래스 내부에서 선언, 정의한 것을 다른 클래스가 물려받는 것을 의미합니다. 이는 객체의 성질을 표현하는 것에도 도움이 되지만 코드의 재사용성을 높이는 것에도 도움이 됩니다.

 

캡슐화는 연관이 있는 특성을 묶어 내부에서 구현한 뒤, 구체적인 구현부는 외부에 공개하지 않고 기능 사용에 관련된 인터페이스만 공개하는 것을 의미합니다. 캡슐화가 잘 된 객체의 경우 외부에서 객체의 성질을 함부로 변형할 수 없지만, 필요에 따라 객체에서 제공하는 기능은 사용할 수 있기 때문에 안정성, 유지보수성 등을 높일 수 있습니다. 

 

추상화는 클래스를 상속받을 대상의 공통적인 속성을 추려내는 것을 의미합니다. 자식 클래스들이 모두 공통적인 속성을 보유하고 있다면, 해당 속성과 관련된 메서드, 멤버 변수 등을 부모 클래스에서 선언한 뒤 자식 클래스에서 이를 정의하는 방향으로 설계할 수 있습니다. 이러한 과정을 거치면 다양한 자식 클래스가 통일성 있게 작동할 수 있습니다.

 

다형성이란 대상이 다양한 모습으로 표현되고 사용될 수 있는 것을 의미합니다. 상속 관계에 있는 클래스가 부모 클래스로도 표현될 수 있고 자식 클래스로도 표현될 수 있는 것이 다형성의 대표적인 예시입니다.

 

이러한 객체지향의 특징을 살리는 방향으로 코드를 설계한다면 안전하고 유지보수가 용이하며 사용이 편리하고 가독성이 높은 코드를 설계할 수 있습니다.

21. LOD와 밉맵에 대해 설명하라.

더보기

LOD는 특정 기준에 따라 버텍스의 수를 조절하여 매쉬의 디테일 수준을 조절하는 기법입니다. 멀리 있는 대상 혹은 작게 그려지는 대상을 디테일하게 렌더링하는 것은 연산의 낭비로 이어질 수 있기 때문에, 거리, 투영면적 등의 기준에 따라 LOD단계를 조절하여 최적화를 달성할 수 있습니다.

 

밉맵은 LOD와 동일한 기술을 텍스쳐에 적용하는 기술입니다. 디테일하게 렌더링 될 필요가 없는 대상의 텍스쳐 해상도를 낮춰 연산 낭비를 줄이는 기법입니다.

 

두 기술 모두 속도 측면에선 최적화에 큰 도움이 되지만, 단계별 리소스를 별도로 저장해야 하기 때문에 메모리를 많이 사용하게 된다는 단점이 있습니다.

22. 렌더링 파이프라인을 요약해서 설명하라.

더보기

렌더링 파이프라인이란, 데이터로만 존재하는 오브젝트를 모니터에 출력하기 위해 거치는 일련의 과정을 의미합니다. 렌더링 파이프라인은 3D공간에 있는 물체에 WVP 행렬을 곱하여 2D 평면에 투영한 뒤, 뷰포트 행렬을 통해 대상이 그려질 픽셀을 건져내고 쉐이더 등을 통해 최종적인 색상을 결정하는 과정으로 진행됩니다.

23. C#의 static 키워드에 대해 설명하라.

더보기

멤버 변수나 메서드가 인스턴스에 종속되지 않고 클래스에 종속되도록 만들어주는 키워드입니다.

 

static으로 생성된 멤버 변수는 프로세스가 생성되고 초기화되는 과정에서 메모리에 저장되기 때문에 인스턴스가 생성되지 않은 시점에서도 대상을 사용할 수 있게 됩니다. static 멤버 변수는 데이터 영역에 위치하는 만큼, 멤버 변수가 인스턴스별로 생성되는 것이 아니라 모든 인스턴스가 하나의 변수를 공유하여 사용하게 됩니다. 여러 인스턴스가 하나의 변수를 공유하는 만큼 static 멤버 변수의 데이터의 안전성을 보장할 수 있는 방향으로 설계해야 합니다.

 

static 메서드의 경우는 함수를 호출하는데 this가 필요하지 않기 때문에 인스턴스가 없어도 독립적으로 사용할 수 있게 됩니다. 다만, 이러한 이유 때문에 함수 내부에서 this가 필요한 작업은 수행할 수 없다는 단점이 있습니다.

24. (C# )컬렉션이란?

더보기

컬렉션이란, 닷넴 프레임워크에서 지원해주는 데이터 관리용 클래스를 의미합니다.

25. (C#) List와 ArrayList의 차이를 설명하라.

더보기

List는 generic을 기반으로 데이터를 보관하는 자료구조이고, ArrayList는 object를 기반으로 데이터를 보관하는 자료구조 입니다.

 

List는 generic을 사용하는 만큼 선언할 때 명시한 자료형에 해당하는 데이터만 저장할 수 있지만, ArrayList는 C#의 모든 객체가 공통으로 상속받고 있는 object 자료형으로 데이터를 저장하기 때문에 모든 자료형의 데이터를 저장할 수 있습니다.

 

List는 자료형이 한정되어 있는 만큼 ArrayList에 비해 활용성이 다소 제한되어 있지만, 그만큼 안정성이 높으며 boxing, unboxing과정을 거치지 않아 성능 부분에서도 우위에 있다는 장점이 있습니다. 반대로 ArrayList는 안정성이 낮고 boxing, unboxing 과정으로 인해 느린 속도를 보인다는 단점이 있지만 활용성이 매우 넓다는 장점이 있습니다. 

26. BFS와 DFS에 대해 설명하라.

더보기

두 알고리즘 모두 그래프를 완전탐색하는 알고리즘입니다. BFS는 너비우선탐색으로 시작 노드와 가까이에 있는 노드를 우선적으로 탐색하는 알고리즘입니다. 반면, DFS는 노드의 자식노드를 재귀적으로 순회하며 탐색하는 알고리즘입니다. BFS는 가까이에 있는 대상을 DFS보다 먼저 탐색할 확률이 높지만, DFS는 반대로 멀리 있는 대상을 BFS보다 먼저 탐색할 확률이 높다는 차이가 있습니다.

27. 메모리 정렬이란? 

더보기

모든 데이터는 차지하는 메모리 크기의 배수에 해당하는 메모리 주소에 위치해야 합니다. 구조체처럼 메모리에 연속적으로 위치해야 하는 데이터들이 메모리 주소에 올바르게 위치하려면 데이터 사이에 패딩 데이터를 추가하여 데이터가 위치하는 메모리 주소를 맞춰주어야 합니다. 이를 메모리 정렬이라고 합니다. 

28. 내부단편화란?

더보기

메모리 풀링 등을 사용하여 미리 할당한 메모리영역 내에서 메모리가 비효율적으로 사용되는 것을 의미합니다. 실제 필요한 메모리 공간에 비해 불필요하게 많은 메모리를 할당하는 경우 내부 단편화가 발생할 수 있습니다.

29. (유니티) ILtoCPP란?

더보기

프로그래머가 유니티 엔진을 사용할 때는 C#을 사용해 코드를 작성하게 됩니다. 닷넴 프레임워크에서 작동되는 C#은 컴파일 타임에 IL이라는 중간언어로 번역되고  IL이 런타임에 기계어로 변환되는 과정을 거치지만, 유니티 엔진은 그러한 과정을 거치지 않고 IL을 다시 C++로 디컴파일 한 뒤 다시 기계어로 컴파일하는 과정을 거칩니다. 이 작업을 통해 실제로 실행되는 게임은 C++ 코드로 실행되며 닷넴 프레임워크에 의해 실행될 때에 비해 높은 성능을 가지게 됩니다. 

30. 내적과 외적에 대해 설명하라.

더보기

내적은 두 벡터의 방향이 얼마나 일치하는지를 구하는 수학적 개념입니다. 두 벡터를 내적하게 되면 두 벡터의 길이와 사잇각의 코사인 값을 곱한 스칼라 값을 얻을 수 있습니다. 두 벡터의 길이를 안다면 두 벡터의 사잇각을 구할 수 있기 때문에, 게임 프로그래밍에선 두 오브젝트의 위치 관계를 구하는데 사용할 수 있습니다.

 

외적은 두 벡터가 속한 평면의 법선 벡터를 구하는 수학적 개념입니다. 두 벡터를 외적하게 되면, 두 벡터에 모두 수직인 벡터를 구할 수 있습니다. 이를 활용하여 두 오브젝트의 위치 관계를 구하는데 사용할 수 있습니다.

 

내적의 경우, 대상이 나의 앞에 있나 뒤에 있나를 구할 수 있지만 왼편에 있는지 오른편에 있는지는 구할 수 없기 때문에 외적을 추가적으로 사용하여 나의 왼편에 있는지 오른편에 있는지를 구할 수 있습니다.

31. 프로세스 스케줄링이란?

더보기

CPU의 코어는 동시에 여러 작업을 수행할 수 없습니다. 하지만, 실제 컴퓨터에선 여러 작업이 동시에 수행되어야 하기 때문에 CPU는 프로세스를 작은 작업으로 쪼갠 뒤 이를 고속으로 번갈아 수행하며 여러 프로세스가 동시에 실행되는 것처럼 보이게 해줍니다. 

 

이처럼 여러 프로세스가 번갈아가며 수행될 때 효율성을 높이기 위해선 프로세스를 어떤 순서로 얼만큼씩 작업하는가를 구체적으로 설정할 필요가 있습니다. 이를 구체적으로 설정하는 작업을 프로세스 스케줄링이라 하며 이는 운영체제에 의해 수행됩니다.

32. 스케줄링의 선점형, 비선점형에 대해 설명하라.

더보기

선점형 스케줄링이란 한 작업이 실행되고 있을 때 다른 작업을 수행하기 위해 현재 작업이 중단될 가능성이 있는 스케줄링을 의미합니다. 

 

비선점형 스케줄링이란 현재 실행중인 작업이 완료되기 전까진 다른 작업을 수행하지 않는 스케줄링을 의미합니다.

33. 소켓 통신이란?

더보기

소켓 통신이란, 소켓을 사용하는 통신 방법을 의미합니다. 소켓을 사용하여 서버와 클라이언트가 특정 포트번호를 통해 양방향으로 통신하는 방식으로, TCP 프로토콜의 통신 방법입니다.

34. 무브 시맨틱이란?

더보기

깊은 복사의 단점인 성능 저하를 최소화하기 위해, 다시는 사용되지 않을 것이 확신한 객체를 복사할 때 얕은 복사를 응용한 이동 연산을 사용하는 최적화 기법입니다.

35. auto 키워드

더보기

자료형을 프로그래머가 명시하지 않고 컴파일러의 추론에 맡기는 키워드 입니다. 자료형이 길고 복잡하거나 프로그래머가 추론하기 힘든 상황에 편리하게 사용할 수 있지만, 자료형을 한 눈에 알아볼 수 없기 때문에 가독성이 저하될 수 있다는 단점이 있습니다.

 

스터디를 진행하며 실시한 모의 면접을 정리한 내용입니다.
겹치는 내용을 고려하지 않고 면접을 실시하기 때문에 일자 별로 겹치는 내용이 많을 수 있습니다.

1. 포인터와 참조자의 차이를 설명하라.

더보기

포인터의 경우 nullptr을 가리킬 수 있지만 참조자는 반드시 존재하는 대상을 가리켜야 합니다. 하지만, 포인터는 가리키고 있는 대상을 자유롭게 바꿀 수 있는 반면 참조자는 가리키고 있는 대상을 변경할 수 없습니다. 이러한 차이로 인해 참조자는 포인터에 비해 안전하게 사용할 수 있지만, 포인터만큼 유연하게 사용할 수는 없습니다.

2. C++의 캐스팅은 무엇이 있는가?

더보기

컴파일 타임에 이루어지는 static_cast, 런타임에 작동하는 dynamic_cast, 포인터의 형변환이 가능한 reinterpret_cast, 포인터 변수의 상수성을 제거해주는 const_cast가 있습니다.

3. 해시테이블이란?

더보기

해시 값을 키로 사용하는 자료구조입니다.

4. 해시테이블의 장단점은?

더보기

해싱된 키 값이 배열의 인덱스로 사용되기 때문에 데이터를 탐색하고 접근하는 것이 매우 빠르다는 장점이 있습니다. 다만, 원소의 수가 매우 적을 경우 메모리가 낭비될 수 있다는 단점이 있으며 동일한 해시 값을 가지는 키가 여럿 존재할 경우 성능이 떨어질 수 있다는 단점이 있습니다.

5. 운영체제란?

더보기

사용자가 하드웨어 상호작용, 메모리 관리 등 저수준의 영역을 대신 관리하여 유저가 신경쓰지 않도록 도와주는 프로그램입니다. PC가 부팅될 때 가장 먼저 실행되어 유저가 편하고 안전하게 PC를 사용할 수 있도록 다양한 인터페이스를 제공해줍니다.

6. 메모리 풀링이란?

더보기

여러 개의 오브젝트가 생성될 필요가 있을 때, 필요할 때마다 메모리를 할당하는 것이 아니라 미리 할당해두고 필요한 만큼 사용하는 기법입니다. 런타임에 발생하는 동적 할당을 줄일 수 있으며, 오브젝트를 메모리에 순차적으로 위치시켜 캐시 적중률을 높일 수 있습니다. 이로 인해 성능의 향상을 기대할 수 있지만 불필요하게 많은 메모리를 할당한 경우 내부 단편화가 발생할 수 있습니다.

7. placement new란?

더보기

new 연산자의 경우, 메모리를 할당하고 생성자를 호출하여 값을 초기화한 뒤 할당 받은 메모리의 주소를 타입 캐스팅하여 반환하는 과정으로 진행됩니다. 이 과정 중에서 메모리 영역에 생성자를 호출하여 값을 초기화하는 역할을 하는 것이 placement new입니다. 이미 할당되어 있는 영역에 생성자를 호출하여 값을 초기화할 수 있으며, new 연산자 외부에서도 필요에 따라 사용할 수 있습니다. 주로 메모리 풀링에 사용됩니다. 

8. 캐시 적중률이란?

더보기

CPU가 캐시에서 데이터를 찾을 확률을 의미합니다. 캐시 적중률이 낮을 경우 CPU가 메인 메모리에 접근하는 횟수가 많아지기 때문에 성능의 저하가 발생할 수 있습니다.

9. 고정 소수점과 부동소수점에 대해 설명하라.

더보기

고정 소수점은 2진수로 변환된 실수를 그대로 메모리에 저장하는 방식입니다. 부동 소수점 방식에 비해 정밀도가 높지만 동일한 메모리 크기에서 표현할 수 있는 실수의 범위가 매우 좁다는 단점이 있습니다.

 

부동 소수점 방식은 2진수로 변환된 실수의 정수부가 1이 되도록 소숫점을 앞으로 옮긴 값을 가수부에 저장하고, 소숫점을 옮긴 정도를 지수부에 저장하는 방식입니다. 고정 소수점에 비해 정밀도가 다소 낮지만, 메모리 크기 대비 표현할 수 있는 실수 범위가 고정 소수점에 비해 매우 넓다는 장점이 있습니다.

10. 메모리 구조에 대해 설명하라.

더보기

메모리는 코드 영역, 데이터 영역, 힙 영역, 스택 영역으로 구성되어 있습니다. 코드 영역은 기계어로 번역된 바이너리 코드가 저장되며, 데이터 영역엔 전역 변수, static 변수가 저장됩니다. 힙 영역은 필요에 따라 메모리를 동적으로 할당하여 사용할 수 있는 영역이며 스택 영역은 호출 된 함수의 로컬 데이터를 저장하는 영역입니다. 

11. 변환 색인 버퍼란?

더보기

변환 색인 버퍼란 페이징 테이블의 데이터를 캐싱하는 하드웨어입니다. 페이징 기법으로 가상 메모리를 구현한 환경에선 CPU가 물리 주소에 접근하기 위해 페이징 테이블을 한 번 거쳐야 합니다. 페이징 테이블은 메인 메모리에 위치하기 때문에 CPU는 메인 메모리에 2번 접근해야 하는데, 이로 인해 병목 현상이 발생하게 됩니다. 변환 색인 버퍼는 CPU가 메인 메모리에 직접 접근하는 횟수를 최소화하여 병목현상을 완화하는 역할을 합니다.

12. quicksort란?

더보기

하나의 피봇을 설정한 뒤, 피봇보다 작은 값은 피봇의 왼쪽으로 옮기고 피봇보다 큰 값은 피봇의 오른쪽으로 이동시키는 작업을 피봇에 의해 분할되는 좌우에 재귀적으로 반복하는 방식으로 구현하는 정렬 알고리즘입니다. 평균적으로는 NlogN의 시간 복잡도를 보이지만 피봇에 따라 정렬 속도가 크게 차이나기 때문에 적절한 피봇을 정할 수 없는 상태라면 다른 정렬 알고리즘을 사용하는 것이 유리할 수 있습니다. 

13. 클래스와 객체란?

더보기

객체는 구현하고자 하는 대상이며, 클래스는 객체의 속성을 코드로 작성하여 구체화한 것입니다. 

14. 명령어파이프라인이란?

더보기

명령어 파이프라인이란 CPU의 작업 성능을 극대화하기 위해 명령어를 여러 단계로 분할하여 여러 명령어를 병렬적으로 처리하는 기법입니다.

15. 닷넷 프레임워크란?

더보기

마이크로 소프트에서 제작한 프레임워크로 운영체제에 의존적이지 않은 개발, 실행 환경을 제공해줍니다. 닷넷 프레임워크를 사용할 수 있는 다양한 언어들이 동일하게 작동할 수 있도록 작성된 코드를 IL이라는 중간 언어로 컴파일 한 뒤 런타임에 운영체제에서 이해할 수 있는 기계어로 번역하는 방식으로 작동합니다.

스터디를 진행하며 실시한 모의 면접을 정리한 내용입니다.
겹치는 내용을 고려하지 않고 면접을 실시하기 때문에 일자 별로 겹치는 내용이 많을 수 있습니다.

1. noexcept 키워드에 대해 설명하라.

더보기

함수가 어떠한 예외도 방출하지 않음을 명시하는 키워드입니다. 예외가 발생하지 않음을 확신할 수 있다면, 컴파일러는 그에 맞는 최적화를 진행할 수 있으므로 추가적인 성능 향상을 기대할 수 있습니다.

2. 컨텍스트 스위칭이란?

더보기

CPU를 점유중인 프로세스, 스레드가 다른 프로세스, 스레드로 교체되는 과정을 의미합니다. 잦은 컨텍스트 스위칭은 시스템에 부하를 줄 수 있으므로 이를 최소화하는 방향으로 프로그래밍하는 것이 중요합니다. 

3. 동시성과 병렬성이란?

더보기

동시성이란 물리적으로는 동시에 실행되지 않지만, 동시에 실행되는 것처럼 보이는 것입니다. 하나의 단일코어에서 여러 작업을 처리하는 것이 동시성의 대표적인 예시입니다. 반면, 병렬성은 물리적으로 동시에 실행되는 것을 의미합니다. 다중 코어에서 여러 작업을 처리하는 것이 대표적인 예시입니다. 

4. 템플릿 특수화에 대해 설명하라.

더보기

템플릿은 특정 함수, 클래스가 다양한 자료형에 대응할 수 있도록 도와주는 문법입니다. 템플릿을 사용하염 모든 자료형에 대해 동일한 구조와 기능을 지닌 함수, 클래스 등을 만들 수 있습니다. 템플릿 특수화는 템플릿으로 함수, 클래스 등을 선언한 상태에서 특정 자료형에 대해서는 다른 구조를 취하고 싶을 때 사용합니다. std::vector가 템플릿 특수화를 사용하는 대표적인 예시입니다. std::vector를 boolean타입으로 선언하게 되면 다른 자료형과는 다르게 unsigned int 자료형을 비트단위로 쪼개어 값을 저장하고 사용하게 됩니다.

5. 함수형 프로그래밍이란?

더보기

값을 변경하지 않는 함수를 조합하여 프로그래밍하는 방법론을 의미합니다. 함수 위주의 사용으로 인해 코드의 재사용성이 높습니. 또한, 변수에 쓰기작업을 하지 않기 때문에 멀티 스레드 환경에서 안전하다는 장점도 있습니다. 하지만, 설계가 복잡하고 어렵다는 단점이 있습니다.

6. const 키워드에 대해 설명하고, 위치에 따른 의미를 설명하라.

더보기

const란 대상을 상수화하여 값을 변경할 수 없도록 만드는 키워드입니다. 값형 변수에 붙으면 대상을 변경할 수 없는 상태로 만듭니다. 포인터 변수의 경우엔 앞에 붙을 경우 포인터가 가리키는 대상을 변경할 수 없게 만들며, 포인터 변수의 뒤에 붙으면 가리키는 대상을 상수화합니다.

 

멤버 함수의 경우에도 const를 사용할 수 있습니다. 멤버 함수의 뒤에 const를 붙이게 될 경우 함수 내에서 사용되는 this포인터가 가리키는 대상을 상수화하기 때문에 멤버 변수를 수정할 수 없게 됩니다.  

7. malloc과 new 의 차이를 설명하라.

더보기

malloc는 동적 할당을 할 때 사용하는 함수입니다. 할당 받을 메모리 크기를 인자로 대입하면 동일한 크기의 메모리를 할당하여 가장 앞의 주소를 void*타입으로 반환해줍니다. void* 으로 메모리를 생성하기 때문에 대상의 생성자를 호출하지 않습니다.

 

new는 동적 할당을 할 때 사용하는 연산자입니다. new는 메모리 할당, 생성자 호출, 타입캐스팅의 순서로 이루어지기 때문에 malloc과 달리 반환형이 void*이 아니며, 생성자를 호출해준다는 차이가 있습니다. 또한, 할당 받을 메모리의 크기가 malloc처럼 자유롭지 않고 자료형의 크기에 기반한다는 차이가 있습니다.

8. struct에 char, int가 순서대로 선언되어 있을 때, 해당 구조체의 크기는?

더보기

8바이트의 공잔을 차이합니다. CPU는 데이터를 사용할 때 4바이트 단위로 사용하는 것이 가장 효율적이기 때문에 char타입과 int타입의 크기를 합친 5바이트의 크기로 메모리를 할당하지 않고, char타입에 4바이트를 할당하고, 그 뒤에 int 타입의 메모리를 할당하게 됩니다.

9. static 멤버변수에 대해 설명하라.

더보기

static 멤버변수는 사용 범위가 클래스 내부로 제한되어 있는 static 변수입니다. static 변수인 만큼 데이터 영역에 위치하고 있으며 인스턴스가 아무리 생성되어도 메모리 상에 1개만 존재하게 됩니다.

10. volatile 키워드에 대해 설명하라.

더보기

컴파일러의 최적화를 제한하는 키워드입니다. 컴파일러의 최적화로 인해 작업 결과가 의도와 다를 수 있는 경우에 사용하게 됩니다.

11. Map과 Unordered_map의 차이에 대해 설명하라.

더보기

map과 unordered_map 모두 키와 데이터를 쌍으로 저장하여 사용하기 위한 자료구조입니다.  map은 이진 균형 트리 기반의 자료구조이며, 데이터가 삽입, 삭제될 때마다 정렬을 실행합니다. 반면, unordered_map은 해시 테이블 기반의 지료구조 이며 데이터를 정렬하지 않습니다.

12. Port 넘버란?

더보기

IP와 MAC주소를 통해 PC에 도달하였을 때, 해당 PC에서 여러 프로세스가 실행중이라면 최종 목적지를 식별할 수 없게 됩니다.  이 때, 최종 목적지 프로세스를 식별하기 위해 사용되는 것이 포트 번호입니다. 각 프로세스에 고유의 번호를 부여하여 이를 기반으로 목적지를 식별하게 됩니다. 포트 번호는 실행중인 프로세스 간에 겹치지 않는 것이 중요합니다. 그렇기 때문에 웹 사이트 등에서 이미 사용하고 있는 포트 번호를 파악한 뒤 겹치지 않는 방향으로 설정해야 합니다.

13. 컬링과 클리핑의 차이는? 또한 컬링의 종류를 아는대로 말하라.

더보기

컬링이란 대상을 아예 렌더링에서 제외하는 것이며, 클리핑은 렌더링 된 대상의 일부를 잘라내는 것을 의미합니다.

대표적인 컬링 기법은 백페이스 컬링, 뷰 프러스텀 컬링, 오클루전 컬링, 디스턴스 컬링이 있습니다.

 

백페이스 컬링은 카메라의 시야를 기준으로 반대면을 렌더링에서 제외하는 기법입니다.

뷰 프러스텀 컬링은 카메라의 시야 절두체를 기반으로 외부에 있는 물체를 렌더링에서 제외하는 기법입니다.

오클루전 컬링은 벽 등의 물체에 가려 보이지 않는 대상을 렌더링에서 제외하는 기법입니다.

디스턴스 컬링은 카메라와의 거리가 충분히 멀리있는 대상을 렌더링에서 제외하는 기법입니다.

14. CPU의 구조에 대해 간단하게 설명하라.

더보기

CPU는 크게 ALU, CU, 레지스터로 구성되어 있습니다. CU는 명령어를 해독하여 어떤 부품이 어떤 작업을 실행해야 하는지, 어떤 연산을 실행해야 하는지 등을 결정짓는 역할을 합니다. ALU는 CU로부터 명령받은 산술, 논리 연산을 진행하게 됩니다. 레지스터는 ALU에서 실행된 연산의 결과값이나 연산에 사용될 데이터를 저장하는 역할을 합니다. 

15. 람다함수란?

더보기

람다함수란 이름이 없는 함수를 의미합니다. 다른 함수와 같이 미리 선언과 정의를 해두고 이를 재사용하는 것이 아니라 필요한 상황에 즉석으로 생성하여 사용하게 됩니다. 이러한 차이로 인해 람다함수는 정적 함수와 다르게 스택영역에 위치하게 됩니다. 

 

람다 함수를 잘 활용하면 메모리를 아낄 수 있고, 논리를 쉽게 이해할 수 있다는 장점이 있지만 호출된 함수를 추적하는 것이 어려워 디버깅이 어려워지며 무분별하게 사용하면 코드의 가독성을 저하할 수 있다는 단점이 있습니다. 

16. 벡터의 resize와 reserve의 차이에 대해 설명하라.

더보기

resize는 입력받은 인자의 값만큼 벡터의 size를 증가시키는 역할을 합니다. 반면, reserve는 벡터의 capacity를 입력받은 인자의 값만큼 증가시키는 역할을 합니다. 

17. 동기와 비동기란?

더보기

동기란, 기존의 작업이 종료되어야만 다음 작업을 실행하는 것을 의미합니다. 반면, 비동기는 기존의 작업이 종료되지 않더라도 다음 작업을 실행하는 것을 의미합니다.

18. L-Value 와 R-Value에 대해 설명하라.

더보기

L-Value란 메모리 상에 존재하여 주소를 참조할 수 있는 값을 의미합니다. 반면, R-Value는 호출식이 끝나면 사라지는 임시의 값으로 주소를 참조할 수 없는 값을 의미합니다. 

19. std::atomic이란?

더보기

멀티스레드 환경에서 데이터를 보호하기 위한 기법 중 하나입니다. 변수의 값을 변경하는 경우, 간단한 산술 연산일지라도 값을 읽은 뒤 연산을 수행하고 값을 저장하는 과정이 순차적으로 이루어져야 합니다. 하지만, 중간에 다른 스레드가 개입하여 변수의 값을 변경하게 되면 예측하지 못한 결과가 발생할 수 있습니다. 이를 막기 위해, 반드시 함께 실행되어야 하는 여러 연산을 하나로 묶어 다른 스레드가 중간에 개입할 수 없도록 만들어주는 객체가 std::atomic입니다.

20. IP 와 MAC주소의 차이는?

더보기

IP는 연결된 회선의 주소이기 때문에 접속하는 인터넷에 따라 주소값이 계속 변경되지만, MAC주소는 랜카드에 기록되어 있어 변경되지 않는다는 차이가 있습니다.

21. 함수의 호출 과정을 설명하라.

더보기

함수를 호출하기 전에 현재 스택 프레임의 주소 값을 프레임 포인터 레지스에 저장한 뒤, 함수의 주소를 참조하여 함수를 실행하고, 함수가 종료되면 프레임 포인터 레지스터의 값을 참조하여 기존의 스택 프레임으로 돌아오는 과정으로 진행됩니다.

22. 캐시 지역성이란?

더보기

메인 메모리에 있는 데이터를 캐시 메모리로 복사할 때, 참조한 메모리 주소를 기반으로 근처에 있는 값을 한 번에 가져오게 됩니다. 이 때, 다음 연산에 사용될 변수가 현재 사용된 변수와 메인 메모리에서 가까운 곳에 위치했다면, 캐시 메모리에도 있을 확률이 높아집니다. 이러한 경우 캐시의 공간 지역성이 좋다고 표현합니다. 동일한 데이터를 여러 번 참조하는 경우에도 기존에 참조한 대상이 캐시 메모리에 있을 확률이 높아집니다. 이러한 것을 캐시의 시간 지역성이 좋다고 표현합니다.

23. 인터페이스란?

더보기

인터페이스란, 순수 가상함수를 사용해 특정 메서드의 정의를 강제하는 추상 클래스를 활용해 외부에서 다양한 파생 클래스를 통일된 방식으로 사용할 수 있도록 하는 것입니다.

24. 운영체제란?

더보기

운영체제란, 컴퓨터가 부될 때 가장 먼저 실행되는 소프트웨어입니다. 유저가 하드웨어, 메모리 관리 등의 저수준 영역을 신경쓸 필요 없이 대신 해주는 역할을 합니다. 이로 인해, 유저는 컴퓨터를 안전하고 편리하게 사용할 수 있게 됩니다.

25. 인터럽트란?

더보기

CPU 작업 중 예외가 발생하거나 다른 작업을 우선적으로 처리해야 하는 경우 현재 작업을 잠시 중단하고 우선적으로 다른 작업을 처리하는 것을 인터럽트라고 합니다.

26. SIMD 연산이란?

더보기

SIMD연산이란, 하나의 명령어로 다중 데이터를 처리하는 CPU의 고속 연산 기법입니다. 벡터, 행렬 연산 등에 사용하게 되면 성능 향상을 기대할 수 있습니다.

27. join과 detach에 해 설명하라.

더보기

join이란, 스레드가 작업을 완료할 때까지 메인 스레드의 실행을 중지하는 함수입니다. 반면, detach는 스레드의 작업 완료 상태와 관계없이 메인 스레드는 실행을 유지하게 됩니다. 특정 작업이 모두 완료되어야만 정상적으로 프로그램을 실행할 수 있을 때는 join을 사용하며, 메인 스레드의 흐름과 관계없이 작업을 처리해야 하는 경우엔 detach를 사용하게 됩니다. 

28. 다형성이란?

더보기

하나의 객체, 메서드 등이 다양한 형태로 사용될 수 있는 것을 의미합니다. 상속관계에선 타입캐스팅을 통해 자식 클래스가 부모 클래스로 사용되는 것이나, 함수 오버로딩을 통해 동일한 이름의 함수가 다양하게 사용되는 것이 대표적인 예시입니다.

29. RAII 패턴이란?

더보기

자원의 획득과 해제를 객체의 생애주기와 결합하는 디자인 패턴입니다. 자원의 획득은 객체의 생성자에서 실행되도록 하며, 자원의 해제는 객체의 소멸자에서 실행되도록 설계합니다. 이러한 설계를 통해, 객체가 생성되어 있는 동안 항상 자원이 존재함을 보장할 수 있고 객체가 소멸하는 순간 자원이 해제됨을 보장할 수 있어 메모리 누수를 방지할 수 있으며 안전하게 자원을 사용할 수 있게 됩니다.

30. std::forward에 대해 설명하라.

더보기

인자가 L-Value라면 L-Value의 형태로 리턴하고 R-Value라면 R-Value의 형태로 반환해주는 함수입니다. 템플릿 함수에선 인자가 L-Value인지 R-Value인지 정확히 파악할 수 없어 무브 시맨틱스가 제대로 작동하지 않을 가능성이 있습니다. std::forward를 사용하면 인자의 실제 타입에 맞게 캐스팅되기 때문에 템플릿 환경에서도 무브 시맨틱스가 제대로 작동하도록 할 수 있습니다.

31. 무브 시맨틱이란?

더보기

무브 시맨틱이란, 깊은 복사로 인한 오버헤드를 방지하기 위해 복사가 아닌 이동 연산을 활용하는 기법입니다. 데이터를 얕은 복사한 뒤, 복사되는 대상의 데이터를 지워버리는 방식으로 동작하기 때문에 이동 연산 이후에 사용되지 않을 것이 확실한 객체에 대해서만 사용해야 합니다.

32. explicit 키워드에 대해 설명하라.

더보기

암시적 형변환을 막는 키워드입니다. 대입 연산에서 발생하는 암시적 형변환으로 인해 프로그래머의 의도와 다른 결과가 발생할 수 있는 경우, explicit 키워드를 활용해 명시적 형변환을 강제할 수 있습니다.

33. 뮤텍스와 세마포어의 차이에 대해 설명하라.

더보기

뮤텍스는 임계영역에 1개의 스레드만을 허용하지만, 세마포어는 여러개의 스레드를 허용할 수 있습니다. 뮤텍스의 경우 작업을 진행중인 스레드만이 뮤텍스 객체를 조작할 수 있지만, 세마포어는 외부의 스레드가 세마포어를 조작할 수 있습니다.

34. std::vector에 대해 설명하라.

더보기

std::vector는 C++의 STL에서 제공해주는 배열 기반의 자료구조입니다. 원소가 메모리에 연속적으로 위치하기 때문에 캐시적중률이 높고 임의 접근이 가능하다는 장점이 있지만, 적절한 reserve를 하지 않으면 잦은 동적 할당으로 인한 오버헤드가 발생하거나 메모리 내부 단편화가 발생할 수 있습니다.

스터디를 진행하며 실시한 모의 면접을 정리한 내용입니다.
겹치는 내용을 고려하지 않고 면접을 실시하기 때문에 일자 별로 겹치는 내용이 많을 수 있습니다.

 

1. 가비지 컬렉션이란?

더보기

참조되지 않는 메모리를 자동으로 해제해주는 시스템입니다. C++에선 지원하지 않는 시스템이지만, 언리얼엔진에선 해당 기능을 제공해주고 있습니다. 메모리를 트리구조로 연결한 뒤, 일정 시간마다 트리를 순회하여 참조가 끊긴 메모리를 해제하는 방식으로 진행됩니다.

2. 가비지 컬렉션의 단점은? 

더보기

프로그래머가 직접 메모리를 해제하는 경우 메모리 해제 시점을 정확히 예측할 수 있지만, 가비지 컬렉터를 사용하는 경우 그 시점을 예측하기 힘들다는 단점이 있습니다. 또한, 가비지 컬렉션을 실행하기 위한 추가적인 메모리가 필요하며 메모리 컬렉션으로 인한 성능 저하도 발생할 수 있습니다. 또한, 가비지 컬렉션의 구현 방법에 따라 가비지 컬렉션이 실행되는 동안 가비지 컬렉션을 담당하는 스레드를 제외한 모든 스레드의 작업을 멈추는 과정을 거치기도 합니다. 이로 인해, 가비지 컬렉션이 실행되는 동안 성능이 급격히 저하되는 문제를 겪을 수도 있습니다.

3. 힙과 우선순위 큐의 차이는?

더보기

힙은 이진 트리의 형식을 활용해 빠르게 최대값과 최소값을 찾는 알고리즘입니다. 반면, 우선순위 큐는 우선순위가 가장 높은 대상이 가장 앞에 위치하도록 구현된 큐입니다. 우선순위 큐를 구현하는 여러 방법 중 힙을 사용하는 것이 가장 대표적이지만 힙이 아닌 다른 방식으로도 우선순위 큐를 구현할 수 있습니다.

4. 클래스, 객체, 인스턴스의 차이는?

더보기

구현하고자 하는 대상을 객체라고 하며 코드를 통해 객체의 속성을 구체화한 것을 클래스라고 합니다. 코드로 작성된 클래스가 생성되어 메모리에 적재된 것을 인스턴스라고 합니다. 

5. push_back과 emplace_back의 차이는?

더보기

push_back은 자료구조에 삽입하고자 하는 값을 인자로 받기 때문에 인자 전달 과정에서 복사가 발생할 수 있습니다. 반면, emplace_back은 생성자의 인자를 함수의 인자로 받아 내부에서 객체를 생성하게 됩니다. 인자 전달 과정에서 복사가 발생하지 않아 상황에 따라 성능상 이점을 노릴 수 있습니다.

6. 코루틴과 멀티스레드의 차이는?

더보기

멀티 스레드는 여러 스레드를 활용하여 작업을 물리적으로 병렬 처리하지만 코루틴은 단일 스레드에서 처리되기 때문에 물리적으로는 작업을 순차 처리하게 됩니다. 코루틴은 멀티 스레드에 비해 안전하고 컨텍스트 스위칭이 발생하지 않으며 일반적인 함수 호출에 비해 빠르다는 장점이 있지만 물리적 병렬 작업이 필요한 경우에는 사용할 수 없다는 단점이 있습니다.

7. 앵커란 무엇인가? (언리얼엔진)

더보기

UI가 스크린에 렌더링되는 위치의 기준을 잡아주는 기능입니다. 앵커를 활용하면 UI가 다양한 해상도에서 자연스럽게 렌더링될 수 있습니다.

8. 객체 지향이란?

더보기

현실에서 사몰을 보는 시각으로 프로그래밍을 하고자 하는 방법론입니다. 코드의 흐름이 직선적이지 않고 객체를 넘나들며 실행되기 때문에, 절차지향적 프로그래밍에 비해 처리 속력이 다소 느리지만 코드를 이해하는 것이 매우 직관적이라는 장점이 있습니다.

9. 메시와 머티리얼에 대해 설명하라.

더보기

메시란 대상이 화면에 어느만큼 그려져야 하는지를 결정짓는 데이터입니다. 여러 개의 버텍스로 구성되어 있으며, 버텍스의 위치, 회전, 크기 정보에 따라 렌더링되는 범위가 결정됩니다. 반면, 머티리얼은 메시에 의해 정해진 영역을 어떤 색상으로 그려야 하는지를 결정짓는 데이터입니다. 텍스쳐, 빛 연산 등 색상에 영향을 미치는 모든 요소를 포함하는 개념입니다.

10. 렌더링 파이프라인의 전체적인 과정에 대해 설명하라.

더보기

수치로만 가지고 있는 데이터 정보를 화면에 그려질 색상 정보로 변환하는 과정을 렌더링 파이프라인이라고 합니다. 필수 단계로는 인풋 어셈블러, 버텍스 쉐이더, 레스터라이저, 픽셀 쉐이더, 아웃풋 머저 단계가 있습니다.

 

인풋 어셈블러 단계에선 버텍스 버퍼와 인덱스 버퍼를 참조하여 도형을 조립해 버텍스 쉐이더로 전달하게 됩니다. 또한, CPU에서 전달받은 버텍스 정보와 쉐이더에서 사용할 버텍스 정보를 매칭하기 위해 데이터에 시맨틱스를 부여하는 작업도 거치게 됩니다.   

 

버텍스 쉐이더 단계에선 버텍스의 좌표에 월드 행렬, 뷰 행렬, 프로젝션 행렬을 곱해 원평면에 투영하는 과정을 거칩니다. 해당 과정은 프로그래밍 가능 단계이기 때문에 추가적인 연산이 필요하다면 코드를 작성하여 연산을 추가할 수 있습니다. 

 

레스터 라이저 단계에선 렌더타겟에서 실제로 렌더링 될 위치만 추출하는 과정을 거칩니다. 대상의 후면을 컬링하거나 렌더타겟의 범위 밖으로 넘어가는 부분을 클리핑합니다. 또한, 렌더타겟에서 실제로 렌더링이 될 위치만을 추출하여 픽셀 쉐이더에서 불필요한 연산을 거치지 않도록 도와주는 역할을 합니다. 또한, 버텍스의 데이터를 픽셀 단위로 사용할 수 있도록 보간해주는 역할도 합니다.

 

픽셀 쉐이더는 레스터라이저 단계에서 렌더링을 하기로 정해진 위치의 색상을 정하는 단계입니다. 프로그래밍 가능 단계이므로 텍스쳐를 사용하여 색상을 결정할 수도 있고, 빛 연산 등의 추가적인 연산을 수행할 수도 있습니다.

 

아웃풋 머저는 픽셀 쉐이더에 의해 결정된 색상과 기존에 그려져 있던 색상을 어떻게 처리할 지 결정하는 단계입니다. 알파 블렌딩, 뎁스 스텐실 테스트 등을 거쳐 최종적으로 렌더타겟에 저장될 값을 결정짓게 됩니다.

11. 오브젝트 풀링이란?

더보기

오브젝트가 필요할 때 마다 동적으로 할당하는 것은 런타임 성능의 저하를 유발할 수 있기 때문에 로드 중에 미리 대량의 메모리를 할당해놓고 필요할 때마다 사용하는 최적화 기법을 오브젝트 풀링이라고 합니다. 오브젝트 풀링은 동적 할당으로 인한 오버헤드를 줄일 수도 있지만 외부 단편화에 강하고 오브젝트의 캐시 적중률을 높일 수 있기 때문에 적절히 사용하면 높은 성능 향상을 기대할 수 있습니다. 다만, 메모리를 할당해놓고 일부밖에 사용하지 않는다면 내부 단편화가 발생할 수 있기 때문에 필요한 만큼만 할당하는 것이 중요합니다.

12. 반투명한 물체를 처리하는 방법은?

더보기

 반투명 물체의 경우 앞에 있는 물체가 먼저 그려질 경우 뒤에 있는 물체의 일부분이 의도치 않게 클리핑될 수 있습니다. 이를 막기 위해 카메라의 위치를 기준으로 멀리있는 물체부터 정렬하여 렌더링하는 알파 소팅 기법을 사용할 수 있습니다. 하지만, 알파 소팅의 경우에도 완벽하게 문제를 해결하지는 못합니다.

 

그러한 경우에는 물체가 불투명임을 가정하고 렌더링하여 렌더타겟에 깊이를 저장한 뒤, 해당 깊이 값을 활용해 불투명 물체를 다시 렌더링하여 문제를 해결할 수 있습니다. 하지만, 이 경우 드로우 콜이 2번 발생하기 때문에 반드시 필요한 상황에만 사용해야 합니다. 

13. UDP 와 TCP 의 차이에 대해 설명하라.

더보기

UDP는 비연결 지향형 프로토콜이며 TCP는 연결 지향형 프로토콜입니다. TCP의 경우 안전한 데이터 송수신을 위해 3 way HandShake로 서로 연결하며 4 Way handShake로 연결을 해제합니다. 또한, 패킷의 송수신 과정에서 흐름제어, 혼잡제어 등의 기법을 사용해 패킷의 신뢰성을 보장하고 있습니다. 반면, UDP의 경우 별도의 처리 없이 목적지로 데이터를 보내기 때문에 데이터의 신뢰성을 보장되어 있지 않습니다. 반면 UDP는 어떠한 처리도 거치지 않는 만큼 송수신 속력이 TCP에 비해 매우 빠르다는 장점이 있습니다. 이러한 특성으로 인해 TCP는 파일 전송 등 신뢰성이 중요한 경우에 사용되며 UDP는 동영상 스트리밍 등 데이터의 신뢰성보다 속력이 중요한 경우에 주로 사용됩니다.

14. 상속을 사용하는 이유는?

더보기

상속의 주 목적은 코드의 재사용입니다. 한 클래스에 정의된 내용을 다른 클래스에서도 동일하게 정의해야 한다면 클래스의 상속을 통해 기능을 물려줄 수 있습니다. 또한, 상속을 사용하면 객체지향의 특징인 다형성을 보장할 수 있다는 장점도 있습니다.

15. 템플릿과 오버로딩의 차이는?

더보기

템플릿은 동일한 기능이 다양한 자료형에 대해 대응할 수 있도록 사용하는 기능입니다. 동일한 기능을 여러 자료형을 대상으로 여러 개 정의하는 것은 매우 번거로운 일이지만, 템플릿을 활용하면 한 번의 정의를 통해 여러 자료형에 대응할 수 있게 됩니다.

 

반면 오버로딩은 동일한 이름의 함수를 다양한 파라미터로 사용할 수 있도록 하는 기능입니다. 본질적 기능이 유사한 경우 동일한 이름을 사용하여 작명의 번거로움을 피할 수 있습니다.

16. 오버로딩과 오버라이딩의 차이는?

더보기

오버로딩은 이름은 동일하되, 파라미터가 다른 경우에 사용하는 문법입니다. 반면, 오버라이딩은 상속 관계에서 자식클래스가 부모 클래스의 함수를 재정의하여 새롭게 사용하는 문법입니다.

17. 스마트포인터란?

더보기

메모리의 할당과 해제를 대신 관리해주는 객체입니다. 메모리를 동적으로 할당하고 해제할 때엔 malloc/free 혹은 new/delete를 사용해야 하지만, 프로그래머의 실수로 인해 메모리의 누수가 발생하거나 댕글링 포인터 문제가 발생할 수 있습니다. 이러한 위험없이 안전하게 메모리를 관리할 수 있도록 C++에서 제공해주는 기능입니다. 동적으로 할당된 메모리 영역을 참조하는 스마트 포인터 객체가 증가할 때마다 참조 카운트를 증가시키고 감소할 때마다 참조 카운트를 감소시키며 참조 카운트가 0이 되었을 때 메모리의 할당을 해제하여, 메모리 누수가 발생하지 않도록 관리해줍니다.

18. 32비트 , 64비트 운영체제 차이

더보기

32비트 운영체제는 메모리 주소를 32비트로 저장하며, 64비트 운영체제는 메모리 주소를 62비트로 저장합니다. 32비트로 메모리 주소를 저장하는 경우 표현할 수 있는 주소의 한계로 인해 메인 메모리의 용량이 아무리 크더라도 대략 4GB를 넘어서면 사용할 수 없게 됩니다. 반면, 64비트 운영체제는 표현할 수 있는 주소의 범위가 훨씬 넓기 때문에 메인 메모리의 용량을 최대한으로 사용할 수 있습니다.

19. 유저 영역 vs 커널 영역

더보기

유저 영역은 운영체제에서 유저가 사용하고 접근할 수 있는 영역을 의미하며, 커널 영역은 운영체제의 중요한 기능을 담고 있어 유저가 함부로 접근할 수 없도록 제한되어 있는 영역을 의미합니다.

20. 가상메모리란?

더보기

SSD, HDD 등 보조 기억 장치의 도움을 받아 메인 메모리의 물리적 용량보다 더 큰 용량을 사용할 수 있게 해주는 기법입니다. 프로세스의 데이터 중 실제로 사용되는 데이터만 메인 메모리에 적재하고 사용되지 않는 데이터는 보조 기억 장치에 적재하는 방식으로 동작합니다.

21. 페이징 기법이란?

더보기

가상 메모리를 구현하는 대표적인 방식입니다. 메인 메모리와 프로세스를 동일한 크기의 조각으로 잘게 쪼개놓은 뒤, 프로세스의 조각중 CPU에서 사용되어야 하는 조각을 메인 메모리에 순차적으로 적재하며 동작합니다. CPU에서 프로세스 조각의 물리적 주소에 접근할 수 있도록 운영체제는 페이지 테이블을 생성하여 프로세스의 가상 주소와 물리적 주소를 저장하여 보관합니다. 

 

페이징 기법은 외부 단편화가 발생하지 않는다는 장점이 있지만, 프로세스의 가장 끝에 있는 조각에서 내부 단편화가 발생할 수 있습니다. 또한, CPU에서 메인 메모리에 2번이나 접근해야 하는 상황으로 인해 성능 저하가 발생할 수 있습니다. 

23. 변환색인버퍼란?

더보기

페이징 기법의 문제를 해결하기 위해 사용하는 하드웨어입니다. 페이징 기법은 CPU가 프로세스 데이터의 물리적 주소에 접근할 수 있도록, 가상 주소 테이블을 메인 메모리에 보관하며 사용합니다. CPU에서 프로세스 데이터의 물리적 주소에 접근하기 위해, 가상 주소 테이블에 접근하여 물리적 주소를 알아낸 뒤 물리적 주소에 접근하게 됩니다. CPU보다 처리 속도가 느린 메인 메모리에 2번이나 접근해야 하는 이유로 병목 현상이 크게 발생할 수 있습니다. 이러한 병목현상 문제를 최소화하기 위해 변환 색인 버퍼라는 캐시 메모리에 가상 주소와 물리적 주소를 캐싱하여 사용하게 됩니다.

22. 세그멘테이션 기법이란?

더보기

가상 메모리를 구현하는 기법 중 하나입니다. 프로세스의 데이터를 연관이 있는 것끼리 묶어 여러 세그멘트로 나눈 뒤 세그멘트 별로 메모리에 적재하여 사용하게 됩니다. 세그먼트의 크기가 일정하지 않아, 페이징 기법처럼 순차적으로 메모리에 적재할 수 없어 적재할 때마다 크기만큼 메모리를 할당하는 과정을 거치게 됩니다. 이로 인해, 내부 단편화는 전혀 발생하지 않지만 잦은 메모리 할당과 해제로 인해 외부 단편화가 발생할 수 있습니다. 세그멘테이션 기법도 페이징 기법처럼 가상 주소와 물리 주소를 매핑하는 테이블을 보유하지만 페이지 테이블에 비해 크기가 매우 작아 메인 메모리가 아닌 레지스터에 위치하고 있습니다.

24. 캐시 메모리란?

더보기

CPU와 메인 메모리의 작업 처리 속도 차이로 인해, CPU가 메인 메모리에 직접 접근하게 되면 병목 현상이 발생할 수 있습니다. 이러한 병목 현상을 완화하기 위해, 메인 메모리보다 속력이 빠른 캐시 메모리가 CPU와 메인 메모리 사이에 위치하고 있습니다. CPU가 메인 메모리에 접근하게 되면, 접근한 주소와 근접한 데이터를 캐시 메모리에 복사하여 저장합니다. 이후, CPU는 캐시 메모리를 우선적으로 탐색하게 되고 원하는 데이터가 캐시 메모리에 없다면 메인 메모리에 접근하여 다시 데이터를 가져와 캐시 메모리를 갱신하게 됩니다. 이러한 과정을 통, CPU가 메인 메모리에 직접 접근하는 횟수를 줄여 병목 현상으로 인한 성능 저하를 최소화할 수 있습니다.

25. 캐시적중률이란?

더보기

캐시 메모리에서 원하는 데이터를 발견할 확률을 의미합니다. CPU에서 필요한 데이터를 캐시 메모리에서 탐색할 때, 데이터를 발견하는 것을 캐시 적중이라고 하며 발견하지 못하는 것을 캐시 미스라고 합니다. 캐시 미스가 발생하게 되면, CPU는 다시 메인 메모리에 접근하여 캐시 메모리를 갱신하게 됩니다. 즉, 캐시 미스가 많이 발생할수록 CPU는 메인 메모리에 자주 접근하게 되고 캐시 적중이 많이 발생할수록 CPU는 메인 메모리에 적은 횟수로 접근하게 됩니다. 캐시 적중이 얼마나 많이 발생하는 가를 캐시 적중률이라고 하며, 성능 저하를 최소화하기 위해선 캐시 적중률을 높이는 방향으로 프로그래밍하는 것이 좋습니다.

26. volatile 키워드란?

더보기

volatile 키워드란, 컴파일러의 최적화를 제한하는 키워드입니다. 컴파일러는 프로그램이 최선의 속도로 실행될 수 있도록 프로그래머가 작성한 코드를 일부 변경하여 최적화를 달성하곤 합니다. 하지만, 이러한 최적화로 인해 프로그래머의 의도와 실행결과가 달라지는 경우가 있습니다. 이러한 상황을 방지하기 위해 volatile 키워드를 변수 앞에 붙혀 해당 변수에 대한 컴파일러 최적화를 제한할 수 있으며, 이로 인해 프로그래머의 의도와 실행 결과를 일치시킬 수 있습니다.

27. inline 키워드란?

더보기

함수 호출로 인한 오버헤드를 줄이기위해, 컴파일 타임에 함수의 호출 코드를 함수의 내부 코드로 치환하는 키워드입니다. 

28. 함수 뒤에 붙는 const 를 설명하라.

더보기

함수 뒤에 const를 붙이게 되면 해당 함수 내에서 멤버 변수의 값을 변경할 수 없게 됩니다. 함수 뒤에 const를 붙이게 되면 this 포인터가 가리키는 대상이 상수화되어, 함수 내부에서 멤버변수의 값을 변경할 수 없게 됩니다. 이러한 동작 원리 때문에, 내부에서 전역변수나 다른 클래스의 멤버 변수 값은 얼마든지 변경할 수 있으며 전역 함수의 뒤에는 const를 붙일 수 없습니다.

29. L-Value와 R-Value를 설명하라.

더보기

L-Value는 메인 메모리에 존재하며 스코프를 벗어나기 전까지 사라지지 않는 값입니다. 반면, R-Value는 메인 메모리에 존재하지 않으며 호출식 이후에는 사라지는 값입니다.

30. this 콜이란?

더보기

멤버 함수를 호출할 때, 함수를 호출하는 인스턴스를 식별하기 위해 첫 번째 인자로 객체의 주소를 대입하는 방식입니다. 

31. 멀티 스레드와 멀티 프로세스에 대해 설명하라.

더보기

멀티 스레드는 하나의 프로세스에서 여러 스레드를 사용하는 것을 의미하며, 멀티 프로세스는 동시에 여러 프로세스를 실행하는 것을 의미합니다.

 

프로세스는 각각 별도의 메모리 공간을 보유하고 있기 때문에 하나의 프로세스에 문제가 발생하더라도 다른 프로세스에 영향을 주지 않지만, 스레드는 스택 영역을 제외한 메모리를 모두 공유하기 때문에 하나의 스레드에서 문제가 발생하면 프로세스 전체에 영향을 줄 수 있다는 차이가 있습니다.


또한, 프로세스는 모든 메모리 영역이 독립되어있는 만큼 컨텍스트 스위칭의 비용이 크지만 스택 영역만을 독립적으로 보유하고 있는 스레드의 컨텍스트 스위칭은 상대적으로 가볍다는 차이가 있습니다. 

32. 뮤텍스와 세마포어의 차이는?

더보기

뮤텍스는 임계 영역에 1개의 스레드에 대해서만 접근을 허용하지만, 세마포어는 여러 개의 스레드가 접근하도록 설정할 수 있습니다. 또한, 뮤텍스는 뮤텍스 객체가 lock과 unlock 권한을 가지고 있지만, 세마포어는 자체적으로 lock과 unlock 권한을 가지고 있지 않습니다.

33. IPC란?

더보기

IPC는 프로세스 간의 통신을 의미합니다. 멀티 프로세스 환경에서 각 프로세스는 메모리 영역이 철저히 분리되어 있기 때문에 일반적인 방법으로는 서로 통신할 수 없지만, 커널 영역을 거치면 특수한 방법으로 통신할 수 있게 됩니다.

34. unnamed PIPE vs named PIPE

더보기

PIPE란, 멀티 프로세스 환경에서 프로세스 끼리 통신하기 위해 사용되는 기법 중 하나입니다. PIPE라는 공간에 한 프로세스가 쓰기 작업을 하면 다른 프로세스는 쓰여진 값을 읽는 방식으로 통신하게 됩니다. PIPE에 이름이 부여되어 있으면 named PIPE라고 하며 이름이 부여되어 있지 않다면 unnamed PIPE라고 합니다. unnamed PIPE의 경우 PIPE에 이름이 부여되어 있지 않으므로 하나의 PIPE에 프로세스가 연결되어 있어야 합니다. 이러한 문제로 인해, 자식 부모 관계로 얽혀 있는 프로세스 간의 통신에 사용하게 됩니다. 반면 named PIPE는 PIPE에 이름이 부여되어 있어 프로세스가 원하는 PIPE에 직접 접근할 수 있어 관련없는 프로세스 간의 통신에도 사용할 수 있습니다. 

35. IP와 MAC 이란?

더보기

네트워크에 연결되어 있는 장치들을 식별하기 위해 부여되는 식별 번호입니다. IP는 장치의 Lan Card가 연결되어 있는 회선에 부여되는 주소이며, MAC주소는 Lan Card 자체에 부여되어 있는 주소입니다. IP는 회선에 부여되어 있기 떄문에, 장치가 접속하는 인터넷이 변할 때마다 함께 변하게 됩니다. 반면, MAC주소는 하드웨어에 기록되어 있기 때문에 Lan Card를 교체하지 않는 한 변하지 않는 주소입니다. 

36. IPv4 과 IPv6의 차이는?

더보기

IPv4의 경우 IP를 8비트의 숫자 4개가 엮인 32비트의 형태로 주소를 표현합니다. 반면, IPv6의 경우 16비트의 숫자 8개가 엮인 128비트의 형태로 주소를 표현합니다. IPv4로 표현할 수 있는 주소 범위 내에선 현대에 사용되는 모든 장치에 고유 주소를 부여하는 것이 어렵기 때문에 보다 넓은 범위로 주소를 표현하기 위해 IPv6 방식이 탄생하였습니다. 하지만, 현대에 사용되고 있는 IPv4 기반의 네트워크 통신을 IPv6 기반으로 변경하는 것은 매우 큰 비용이 발생하기 때문에 IPv6는 매우 제한된 환경에서만 사용되고 있습니다. 

37. 포트넘버란?

더보기

포트 넘버란, IP, MAC 주소 만으로는 데이터의 도착지를 식별할 수 없는 경우 최종 목적지를 식별하기 위해 추가적으로 사용되는 주소입니다. 데이터가 도착한 PC에서 여러 개의 프로세스가 실행중인 경우 최종적으로 어느 프로세스에 도달해야 할 지 IP, MAC 주소 만으로는 식별할 수 없기 때문에 프로세스에 포트번호를 부여하여 최종적인 목적지를 판별하게 됩니다.

38. 프로세스의 메모리 구조를 설명해라.

더보기

프로세스는 코드 영역, 데이터 영역, 힙 영역, 스택 영역으로 분할되어 있습니다. 코드 영역엔 프로세스를 프로그래밍할 때 작성된 코드 전체가 저장되어 있으며, 데이터 영역엔 전역 변수, 문자열 리터럴 등 한 번 메모리에 저장되면 프로세스가 종료될 때 까지 삭제되지 않는 데이터들이 저장되어 있습니다. 힙 영역은 런타임에 동적으로 할당하여 사용할 수 있는 메모리 영역이며, 스택 영역엔 호출된 함수 내부에서 사용되는 로컬 데이터가 저장됩니다. 

39. 문자열 리터럴은 L-Value인가 R-Value인가?

더보기

문자열 리터럴은 일반적인 상수 리터럴과 다르게 데이터 영역에 저장되어 있으며 그 주소값을 참조할 수 있습니다. 즉, 문자열 리터럴은 L-Value 입니다.

40. 무브 시맨틱이란?

더보기

더이상 사용되지 않을 것이 확실한 객체에 대해 깊은 복사가 아닌 얕은 복사를 한 뒤 대상의 데이터를 지워 소유권을 완전히 이전시키는 기법입니다. 깊은 복사가 발생하지 않기 때문에 성능상의 이점이 있으며, 복사되는 객체의 데이터를 지워버림으로써 얕은 복사로 인한 댕글링 포인터 문제도 발생하지 않습니다.

41. 플레이어를 기준으로 몬스터가 어디에 있는지 알아내는 방법

더보기

대표적으로 내적과 외적을 사용하는 방법이 있습니다. 플레이어의 위치에서 몬스터를 향하는 벡터와 플레이어의 전방 벡터를 내적하여 몬스터가 앞에 있는지, 뒤에 있는지 구할 수 있습니다.

 

또한, 플레이어 위치에서 몬스터를 향하는 벡터와 플레이어의 위쪽을 향하는 벡터를 외적하면 몬스터가 좌측에 있는지, 우측에 있는지를 구할 수 있습니다. 

42. 행렬연산을 사용하는 이유는 무엇인가?

더보기

벡터를 사용해 연산하는 것보다 훨씬 간단하게 연산 결과를 구할 수 있으며, 하나의 행렬에 모든 정보를 누적하여 저장하기 때문에 메모리를 절약할 수 있기 때문입니다. 또한, 4x4 행렬의 경우 SIMD 연산을 통해 추가적인 성능 향상을 기대할 수 있습니다.

43. 노말맵이란?

더보기

색상 정보가 아닌 노말 정보를 가지고 있는 텍스쳐를 의미합니다. 주로 픽셀 단위로 빛을 계산하기 위해 사용합니다. 

44. 탄젠트 스페이스란?

더보기

폴리곤의 법선을 수직축으로 하는 평면공간입니다. 입체 도형의 법선 정보를 월드 좌표계의 값으로 저장하여 사용하게 되면, 오브젝트가 아무리 회전하더라도 법선 값은 변하지 않아 조명 효과가 부자연스럽게 적용됩니다. 반면, 노말 값을 탄젠트 공간의 값으로 저장하게 되면 현재 폴리곤의 접선과 종법선을 활용해 노말값을 동적으로 계산하여 물체의 회전 정보에 따라 자연스럽게 법선을 매핑할 수 있게 됩니다.

 

하지만, 탄젠트 공간의 노말맵을 사용하게 되면 런타임에 연산이 추가되는 만큼 성능 면에서 손해를 볼 수 있기 때문에, 회전할 가능성이 없는 정적 오브젝트에 대해선 월드 공간의 노말 맵을 사용하는 것이 좋습니다.

45. G버퍼란?

더보기

렌더타겟이 지니고 있는 데이터 버퍼를 의미합니다. 해당 버퍼에는 색상 값이 저장될 수도 있고, 노말 값이 저장될 수도 있으며, 깊이 값이 저장될 수도 있습니다.

46. AABB충돌이란? 문제점은?

더보기

두 사각형 혹은 정육면체 간의 충돌을 탐지하는 방법입니다. 두 도형의 좌표 차와 길이 합을 비교하여 충돌을 탐지합니다. 연산이 매우 간단하지만 회전한 사각형, 정육면체에 대해서는 올바르게 작동하지 않는다는 문제점이 있습니다.

47. OBB충돌이란?

더보기

AABB 충돌로 탐지할 수 없는 회전한 도형의 충돌을 탐지하기 위한 방법입니다.두 도형을 x, y, z 축에 대해 투영한 뒤, 두 도형의 투영 공간이 겹치는 지 여부를 통해 충돌을 탐지합니다. 회전한 도형에 대해서도 충돌을 탐지할 수 있지만 연산량이 많다는 단점이 있습니다. 

48. 드로우콜이란?

더보기

CPU에서 GPU에 렌더링 연산을 명령하는 것을 의미합니다. GPU가 이해할 수 있는 명령어 형태로 변환하는 과정이 상당히 오래걸리기 때문에 CPU에 병목 현상을 가져올 수 있습니다. 그렇기 때문에, 드로우 콜을 최소화하는 것은 최적화에 있어 매우 중요한 요소입니다.

49. 드로우콜 최적화는 CPU최적화인가 GPU최적화인가

더보기

드로우 콜은 CPU 최적화입니다. CPU에서 GPU로 명령을 보내는 작업이기 때문에, 드로우 콜을 줄일수록 CPU는 다른 연산에 시간을 더 투자할 수 있게 됩니다. GPU 최적화 기법은 대표적으로 LOD, 밉맵 등이 있습니다.

50. 쿼터니언이란?

더보기

오일러 각을 사용했을 때 발생할 수 있는 짐벌락 현상이나 회전 보간의 비정확성을 해결하기 위해 회전 연산에 사용하는 수학적 개념입니다.

51. 짐벌락이란?

더보기

회전 축이 겹침으로써 한 축이 회전 능력을 소실하는 현상입니다..

52. 픽셀충돌을 사용하는 이유는?

더보기

복잡한 지형에 대한 충돌 처리를 간단하게 수행할 수 있기 때문입니다.

53. 싱글톤 패턴이란?

더보기

객체의 인스턴스가 프로세스 내에 단 1개만 존재하도록 설계하는 디자인 패턴입니다. 생성자와 소멸자를 private 으로 설정하여외부에서 호출되는 것을 막은 뒤, 객체 내부에서 static 변수로 인스턴스를 생성하는 방식으로 구현됩니다. 

54. 멀티스레드 환경에서 싱글톤의 문제점

더보기

싱글톤 클래스의 인스턴스를 동적 할당하는 방식으로 구현한다면 멀티 스레드 환경에서 객체가 여러개 생성되는 문제가 발생할 수 있습니다. 여러 스레드가 동시에 인스턴스를 반환하는 함수를 호출하게 되면, nullptr 검사 조건문을 여러 스레드가 동시에 통과하게 되어 여러번 동적 할당이 발생하게 됩니다. 이러한 상황을 막기 위해 해당 함수 내부에 lock()과 unlock()을 추가하게 되면 오버헤드로 인해 멀티 스레딩의 능률이 매우 저하될 수 있습니다.

 

이를 해결하기 위한 대표적인 방법으로 싱글톤 클래스를 지역 static 변수로 생성하는 방법이 있습니다.  

55. 전방선언이란 무엇인가? 사용하는 이유는?

더보기

전방 선언이란, 헤더파일을 참조하지 않더라도 자료형을 사용할 수 있도록 전방에 미리 선언해주는 기법입니다. 다만, 자료형이 선언만 되어 있는 형태이기 때문에 멤버 함수 등 자료형에 포함된 기능을 사용할 수는 없습니다. 전방 선언을 사용하는 이유는 불필요한 헤더의 참조를 막아 순환 참조를 방지함과 동시에 빌드 시간을 단축시키기 위해서입니다.

56. 네임맹글링이란?

더보기

네임 맹글링이란 컴파일러가 함수나 변수의 이름을 변경하는 것을 의미합니다. C++의 경우 함수 오버로딩과 같이 동일한 이름의 함수가 존재할 수 있기 때문에 이를 구별하기 위해 이름을 의도적으로 변환하는 과정을 거치게 됩니다. 하지만, C언어의 경우 네임 맹글링을 사용하기 때문에 C언어 개발 환경에선 C++ 라이브러리를 사용할 수 없게 됩니다. 이를 해결하기 위해, C++ 로 코드를 작성할 때 extern "C" 키워드를 함수 앞에 붙여 함수의 네임 맹글링을 제한할 수 있으며, C언어에서도 C++ 컴파일러에 의해 컴파일된 라이브러리를 사용할 수 있게 됩니다.

57.다중상속의 문제점

더보기

상속받은 여러 클래스에 각각 동일한 이름의 함수 혹은 변수가 정의되어 있다면 하위 클래스에선 어떤 클래스의 함수 혹은 변수를 사용해야 할 지 예측할 수 없는 문제가 발생합니다. 

58. 라운드 로빈 알고리즘이란?

더보기

우선순위에 따라 차등하게 작업을 실행하는 것이 아니라, 모든 작업을 동일한 시간만큼 평등하게 실행하는 알고리즘입니다. 우선순위에 의한 기아 현상은 발생하지 않지만, 중요한 작업과 중요하지 않은 작업이 똑같이 실행되는 만큼 작업이 효율적으로 분배되지 못한다는 단점이 있습니다.

59. 컨텍스트 스위칭이란

더보기

CPU에서 작업중인 프로세스, 스레드를 다른 프로세스, 스레드로 교체하는 과정 전체를 의미합니다. 잦은 컨텍스트 스위칭은 오버헤드의 원인이 되기 때문에 멀티 프로세스, 멀티 스레드 프로그래밍에선 컨텍스트 스위칭을 최소화하는 방향으로 설계해야 합니다.

60. 가상 소멸자를 쓰는 이유는 무엇인가?

더보기

자식 클래스의 소멸자를 올바르게 호출하기 위해 사용합니다. 자식 클래스가 부모 클래스로 업캐스팅된 상태에서 객체가 소멸하게 되면, 자식 클래스의 소멸자가 아닌 부모 클래스의 소멸자를 호출하게 됩니다. 이러한 상황을 방지하기 위해, 소멸자를 가상화하여 업캐스팅 된 상황에서도 자식 소멸자가 올바르게 호출될 수 있도록 할 수 있습니다.

61. 클래스와 구조체의 차이는?

더보기

클래스는 접근 제한 지정자의 디폴트 값이 private이고, 구조체는 public이라는 차이가 있습니다.

62. RTTI란?

더보기

런타임에 객체의 실제 타입을 확인할 수 있도록 도와주는 기능입니다. 객체의 가상함수 테이블 가장 앞에 클래스의 정보를 저장하여 사용합니다. 주로, dynamic_cast에 사용됩니다. 

63. 람다함수란 무엇인가? 장단점은 무엇이 있는가?

더보기

람다 함수란 이름이 없는 함수를 의미합니다. 일반적인 함수처럼 미리 선언 및 정의하여 사용하는 것이 아니라, 필요할 때 즉석으로 정의하여 사용하게 됩니다. 여러 번 사용될 가능성이 없는 기능을 람다 함수로 사용하면 메모리를 절약할 수 있다는 장점이 있지만, 람다 함수를 무분별하게 사용하면 가독성이 크게 저하될 뿐더러 디버깅이 매우 어려워집니다.

64. string과 string_view의 차이

더보기

string은 문자열을 저장하는 자료구조입니다. 내부에 배열을 만들어 문자열을 데이터로 보유하고 있습니다. 반면, string_view는 특정 문자열에 대한 포인터만 보유하고 있습니다. string은 실제 문자열을 유연하게 관리하기 위해 사용하는 반면, string_view는 불필요한 문자열의 복사를 방지하기 위해 사용합니다. 

65. RTV란?

더보기

텍스쳐를 렌더타겟으로 사용하기 위해 필요한 GPU의 자원입니다. 렌더타겟 옵션, 플래그 및 텍스쳐 포멧 정보를 담고있습니다. 

 

스터디를 진행하며 실시한 모의 면접을 정리한 내용입니다.
겹치는 내용을 고려하지 않고 면접을 실시하기 때문에 일자 별로 겹치는 내용이 많을 수 있습니다.

 

1. 깊이 버퍼와 스텐실 버퍼란?

더보기

깊이 버퍼란, 카메라의 시야를 기준으로 대상이 얼마나 멀리있는가를 픽셀 단위로 저장하는 버퍼입니다.

0~1사이의 실수값으로 저장되며, 깊이 버퍼를 통해 여러 물체가 겹쳐있는 픽셀의 색상을 결정하게 됩니다.

 

스텐실 버퍼란, 대상을 그릴지 말지를 저장하는 버퍼입니다. 0과 1의 값을 가질 수 있으며, 0인 경우 색상을 저장하지 않고, 1인 경우 색상을 저장하게 됩니다. 스텐실 버퍼를 이용해 거울, TV 등을 구현할 수 있습니다.

2. std::function과 C스타일 함수 포인터의 차이

더보기

std::function은 모든 Callable을 저장할 수 있는 객체입니다. 함수 포인터 뿐만 아니라 람다 함수, 함수 객체도 저장이 가능합니다. std::bind 함수와 함께 사용하면 파라미터와 함수를 묶어 더욱 유연하게 사용이 가능합니다.

 

반면, C스타일의 함수 포인터는 일반적인 함수만 저장 가능할 뿐 람다 함수, 함수 객체 등은 저장할 수 없습니다. 또한, 클래스의 멤버 변수를 사용할 땐 항상 인스턴스의 주소를 함께 사용해야 한다는 불편함이 있습니다.

3. 얕은 복사와 깊은 복사

더보기

얕은 복사란, 값을 그대로 복사하는 것입니다. 변수가 가지고 있는 값을 그대로 복사하기 때문에, 포인터 변수의 경우 가리키는 주소를 복사하는 객체와 복사되는 객체가 동시에 가리키게 됩니다. 이로 인해, 어느 한 쪽에서 가리키고 있던 메모리를 해제할 경우 댕글링 포인터 문제가 발생할 수 있습니다.

 

깊은 복사란, 얕은 복사의 위험성을 해결하기 위해 복사 생성자, 복사 대입자를 새롭게 정의하여 안전하게 사용하는 것입니다. 가리키고 있는 주소를 그대로 복사하는 것이 아니라 새롭게 메모리를 할당하여 복사되는 객체가 가리키고 있던 곳의 값을 복사하게 됩니다. 두 객체는 서로 다른 메모리를 가리키기 때문에, 어느 한 쪽에서 메모리를 해제하더라도 댕글링 포인터 문제가 발생하지 않습니다. 반면, 복사에 추가적인 연산이 필요한 만큼 성능 부분에선 얕은 복사에 비해 불리합니다.

4. 컨텍스트 스위칭이란?

더보기

컨텍스트 스위칭이란 CPU에서 작업 중인 프로세스, 스레드가 다른 프로세스, 스레드로 변환되는 과정을 의미합니다. 컨텍스트 스위칭은 오버헤드를 유발하기 때문에, 멀티 프로세스, 멀티 스레드를 설계할 때엔 컨텍스트 스위칭을 최소화하는 방향으로 설계해야 합니다.

5.  멀티스레드 프로그래밍이 유리한 경우는?

더보기

구현하고자 하는 작업을 병렬로 처리하는 것이 어렵지 않고, 큰 성능 향상을 가져올 수 있을 때 유리합니다. 예를 들어, 서버에서 게임을 실행하는 것과 유저의 접속을 기다리는 것은 멀티 스레드를 활용하여 병렬로 처리하는 것이 유리합니다. 

6. 빅 앤디언 방식과 비교했을 때, 리틀 앤디언 방식의 장점

더보기

데이터를 하위 주소부터 저장하기 때문에, 덧셈 연산 등 하위 주소의 연산을 먼저 실행하는 경우에 높은 성능을 기대할 수 있습니다.

7. 그래픽스 행렬 변환 중 W 나누기를 실행하는 단계는 어디인가?  W를 나누는 이유는?

더보기

W 나누기는 프로젝션 행렬을 곱한 이후에 실행하게 됩니다. 프로젝션 행렬을 곱하게 되면, 물체는 원평면에 투영됩니다. 원평면에 위치한 물체를 정규화된 좌표로 변환하기 위해 W 값으로 나눠 대상의 원근을 제거하게 됩니다.

8. 추상 클래스란?

더보기

순수가상함수가 선언된 클래스를 추상클래스라고 합니다. 해당 객체를 상속받을 객체들의 공통된 속성을 추려내어 미리 선언한 뒤, 추상 클래스를 상속받은 클래스는 해당 함수의 구체적인 내용을 정의하게 됩니다. 예를 들면, 모든 탈것은 움직인다는 기능이 있기 때문에, 부모 클래스에는 Move()라는 순수 가상함수를 선언하여 추상화할 수 있습니다. 이를 상속받은 모든 클래스는 반드시 Move()를 정의해야만 합니다.

9. 코루틴이란?

더보기

실행중인 함수를 원하는 시점에 멈추고 원하는 시점에 다시 실행할 수 있는 기능입니다. 일반적인 함수는 return 문을 만나기 전까지 모든 코드를 연속적으로 실행하지만, 코루틴을 사용한 함수는 원하는 시점에 중단한 뒤 원하는 시점에 돌아와서 작업을 이어나갈 수 있습니다. 코루틴을 통해 작업 사이의 대기 시간을 효율적으로 활용할 수 있기 때문에, 성능의 향상을 기대할 수 있습니다. 

10. Pawn 과 Character의 차이 (언리얼 엔진)

더보기

Character는 Pawn 클래스를 상속받은 클래스입니다. Pawn과 다르게 스켈레탈 매쉬 컴포넌트와 캡슐 컴포넌트, 캐릭터 무브먼트 컴포넌트를 소유하고 있습니다. Character 클래스는 인간형, 동물형 등 복잡한 움직임을 가진 오브젝트를 대상으로 기능을 확장하고 구체화한 클래스입니다. 

11. C++ 타입 캐스팅의 종류

더보기

C++의 타입 캐스팅은 static_cast, dynamic_cast, reinterpret_cast, const_cast 총 4가지가 있습니다. static_cast는 컴파일 타임에 이루어지는 타입 캐스팅입니다. 정수 자료형을 실수형으로 바꾸거나, 상속 관계의 캐스팅을 할 수 있습니다.

 

dynamic_cast는 런타임에 이루어지는 타입 캐스팅입니다. RTTI를 통해 컴파일 타임에 확보된 클래스 정보를 참조하여 캐스팅을 실행하며, 타입 캐스팅이 실패한 경우엔 nullptr을 반환하여 안전하게 사용할 수 있습니다. RTTI 테이블은 가상 함수 테이블의 가장 앞에 위치하기 때문에 가상함수를 포함하고 있지 않은 클래스는 dynamic_cast를 사용할 수 없습니다.

 

반면, static_cast의 경우는 캐스팅이 실패하여도 이를 식별할 방법을 제공하지 않기 때문에 안전하지 않지만 가상 함수 테이블이 필요하지 않고, dynamic_cast에 비해 속도가 빠르다는 장점이 있습니다.   

 

reinterpret_cast는 가장 광범위하게 사용할 수 있는 타입 캐스팅입니다. 포인터 간의 형변환도 가능하며, 값형과 포인터 간의 형변환도 가능합니다. 다만, 정수형을 포인터형으로 변환할 경우 정수값이 그대로 주소값으로 저장되기 때문에 잘못된 메모리 영역을 참조할 수 있다는 위험성이 있습니다.

 

const_cast는 const로 선언된 포인터 변수의 상수성을 일시적으로 제거해주는 타입 캐스팅입니다. 가리키고 있던 변수의 값이 지닌 상수성은 제거되지 않지만, 포인터 변수의 상수성은 제거되어 다른 대상을 가리킬 수 있게 됩니다.

12. 부동소수점이란?

더보기

부동소수점이란 컴퓨터에서 실수를 표현하는 대표적인 방법입니다. 부호부, 지수부, 가수부를 나누어 값을 저장하게 되며 지수부와 가수부의 곱에 부호를 적용하여 실수값을 표현하게 됩니다. 다만, 가수부의 크기를 넘어가는 소수를 표현할 수가 없어 실제 계산 결과와 비교했을 때 오차가 발생할 수 있다는 문제점이 있습니다.

13. 템플릿을 사용하는 이유와 장단점에 대해 설명하라.

더보기

템플릿을 사용하는 이유는 중복되는 코드를 방지하기 위해서입니다. 템플릿을 사용하지 않으면 동일한 기능의 함수를 여러 자료형을 대상으로 여러 번 선언해야 하지만, 템플릿을 사용하면 단 한번의 선언으로 해결할 수 있습니다.

다만, 헤더파일에서만 사용이 가능하고 컴파일 시간이 느려진다는 단점이 있습니다. 또한, 디버깅이 다소 어렵다는 단점이 있습니다.

14. 매크로 함수와 인라인 함수의 차이

더보기

매크로 함수는 사용자가 작성한 코드를 전처리기 단계에서 그대로 치환하여 컴파일됩니다. 반면, 인라인 함수는 컴파일 단계에서 함수가 호출된 곳에 함수의 내부 코드를 그대로 옮겨서 작성하게 됩니다. 인라인 함수는 이를 통해 함수 호출 과정의 오버헤드를 줄이는 것이 목적에 있지만, 매크로 함수는 가독성이나 개발 편의성에 목적을 둔다는 차이가 있습니다.

15. 시스템 콜이란?

더보기

운영체제는 유저가 마음대로 접근할 수 있는 유저 영역과 접근이 제한되어 있는 커널 영역으로 구분되어 있습니다. 커널 영역에 직접적으로 접근할 수는 없지만 커널 영역의 기능을 사용할 수 있도록 운영체제에선 인터페이스를 제공해줍니다. 이 인터페이스를 사용해 커널 영역에 필요한 기능을 요청하는 것을 시스템 콜이라고 합니다. 

16. 빌드 과정에 대해 설명하라.

더보기

빌드는 전처리기, 컴파일, 어셈블러, 링커 순으로 실행됩니다. 전처리기 과정에선 유저가 작성한 코드를 저수준 언어로 변환하기 위한 준비를 하게 됩니다. 주석, 공백 등 불필요한 요소를 제거하고 매크로 구문을 치환하고 헤더파일의 코드 전체를 소스파일 내에 추가하게 됩니다.  

 

다음은 컴파일 과정을 거치게 됩니다. 전처리기 과정을 거친 코드를 저수준의 어셈블리어로 번역하는 동시에 문법상의 오류를 검출하기도 합니다. 이를 통해 어셈블리어로 번역된 코드는 어셈블러 과정을 거치게 됩니다.

 

어셈블러 과정에선 어셈블리어를 0과 1로 이루어진 바이너리 코드로 변환하게 됩니다. 변환된 바이너리 코드는여러개의 오브젝트 파일로 저장됩니다. 

 

이렇게 여러개로 나뉘어진 오브젝트 파일은 링커 과정에서 하나의 프로그램으로 연결됩니다. 또한, 이 과정에서 정적 라이브러리가 프로그램에 함께 묶이게 됩니다. 하나로 묶인 프로그램은 exe파일로 저장되어 빌드를 완료하게 됩니다.

17. 포워드 렌더링과 디퍼드 렌더링에 대해 설명하라.

더보기

포워드 렌더링에선 물체를 그리는 동시에 빛 연산을 함께 실행하게 됩니다. 반면 디퍼드 렌더링은 빛 연산을 바로 수행하지 않고 색, 위치, 노말 정보를 별도의 렌더타겟에 저장한 뒤 마지막에 1번의 빛 연산을 실행하게 됩니다.

 

이로 인해 앞의 물체에 가려지는 부위처럼 빛 연산이 필요없는 곳에 빛 연산을 하지 않을 수 있으며, 빛 연산 횟수가 오브젝트 개수와 독립적이기 때문에 그려지는 오브젝트가 많을수록 빛 연산으로 인한 성능 감소를 줄일 수 있게 됩니다.

 

하지만, 물체의 수가 너무 적거나 앞의 물체에 가려지는 부분이 거의 없다면 포워드 렌더링보다 오히려 낮은 성능을 보이기도 하며, 깊이 버퍼를 제대로 활용할 수 없어 반투명 물체를 처리하기도 매우 힘들다는 단점도 잇습니다.

18. 네임스페이스란?

더보기

변수, 객체, 함수 등에 소속을 부여하는 문법입니다. 예를 들어, A객체와 B객체는 몬스터로 분류가 되고 C객체와 D객체는 플레이어로 분류가 된다면 각각 Monster와 Player라는 네임스페이스를 사용하여 객체를 분리할 수 있습니다. 이로 인해, 변수, 객체, 함수 등을 직관적으로 판단할 수 있게 됩니다.

19. 포인터를 사용하는 이유

더보기

포인터를 사용하면 지역 밖의 변수를 참조할  수 있습니다. 이를 통해 더욱 유연한 프로그래밍이 가능해집니다. 뿐만 아니라, 함수의 인자 등으로 객체를 전달할 때 포인터를 활용하면 복사가 발생하지 않아 성능상 이점이 있습니다.

20. 벡터의 외적과 내적

더보기

백터의 외적은 두 벡터가 포함된 평면의 법선 벡터를 구하기 위해 사용하는 공식이고, 벡터의 내적은 두 벡터의 방향이 얼마나 일치하는 가를 구하기 위해 사용하는 공식입니다.

 

벡터의 내적과 외적을 사용하면 대상이 기준점으로부터 어느 위치에 존재하는지를 구할 수 있습니다. 예를 들어, 몬스터와 플레이어가 충돌했을 때 몬스터가 플레이어를 기준으로 앞에 있는가 뒤에 있는가를 판별하기 위해 내적을 사용할 수 있으며 왼쪽에 있는가 오른쪽에 있는가를 판별하기 위해 외적을 사용할 수 있습니다.

21. 로컬 좌표와 월드 좌표에 대해 설명하라.

더보기

로컬 좌표란 특정 피봇을 (0,0,0)으로 기준잡는 좌표공간입니다. 부모로 설정된 대상이 있을 경우 부모의 좌표가 (0,0)이 되며, 설정되지 않았다면 월드 좌표와 동일한 좌표계에 위치하게 됩니다.

 

반면, 월드 좌표는 부모관계와 상관 없이 항상 정해진 위치를 (0,0,0)으로 기준잡는 좌표공간입니다. 모든 물체가 동일한 기준에 따라 위치하게 됩니다.

22. 포인터와 참조자의 차이

더보기

포인터는 대상의 주소값을 참조하며, 참조자는 대상 그 자체를 참조하게 됩니다. 포인터는 메모리 주소를 참조하는 만큼 8바이트의 고정 메모리 공간이 필요합니다. 반면, 참조자는 상황에 따라 컴파일 타임에 변수 자체로 치환되기 때문에 항상 8바이트의 크기를 가지는 것이 아니라 메모리를 아예 할당하지 않을 수도 있습니다. 

 

또한, 포인터는 const 타입으로 선언되지 않았다면 가리키는 대상을 언제든 바꿀 수 있지만 참조자는 가리키는 대상을 바꿀 수 없으며, 포인터는 nullptr을 가리킬 수 있지만 참조자는 반드시 메모리에 존재하는 대상을 가리켜야만 합니다. 

 

이처럼, 포인터보다는 참조자가 더욱 안전하고 메모리 부분에서도 이점이 있습니다. 그러므로 가능하다면 참조자를 사용하는 것이 좋지만 포인터만큼 유연하게 사용하는 것이 불가능하므로 상황에 맞게 적절하게 사용해야 합니다. 

23. RAII 패턴에 대해 설명하라.

더보기

RAII패턴이란, 자원의 할당과 해제를 객체의 생성, 소멸과 결합시키는 디자인 패턴입니다. 객체의 생성자에서 자원을 할당하고 소멸자에서 해제하도록 하여 객체에 접근 가능한 시점엔 항상 자원이 존재함을 보장할 수 있고, 객체에 접근이 불가능한 시점엔 항상 자원이 해제되었음을 보장할 수 있게 됩니다. 이로 인해, 안정적으로 메모리 관리를 할 수 있게 됩니다.

24. virtual을 생성자에 붙이지 않는 이유는?

더보기

virtual 키워드는 함수를 가상화하는 키워드입니다. 가상화된 함수는 가상함수 테이블을 기반으로 작동합니다. 하지만, 생성자가 호출되기 전에는 객체가 생성되기 전이기 때문에 가상함수 테이블 또한 존재하지 않습니다. 그로 인해, 생성자를 가상함수로 사용하는 것은 불가능합니다. 

 

또한, 생성자는 소멸자와 다르게 자식 클래스의 생성자가 항상 올바르게 호출되므로 가상함수로 사용할 이유도 없습니다.

25. 절차지향과 객체지향에 대해 설명하라.

더보기

절차지향은 컴퓨터의 시선으로 프로그래밍하고자 하는 것이며, 객체지향은 사람의 시선으로 프로그래밍하고자 하는 것입니다. 절차지향의 경우 컴퓨터에서 명령어를 처리하는 순서와 작성된 코드의 순서가 거의 일치하기 때문에, 성능 부분에서 유리함이 있습니다.

 

반면, 객체지향의 경우 객체를 오가며 코드가 실행되기 때문에 절차지향에 비해 느린 처리 속도를 가지고 있습니다. 하지만, 객체지향적으로 설계된 코드는 현실세계와 유사하게 설계되어 있어 절차지향적으로 설계된 코드에 비해 직관적으로 이해할 수 있다는 장점이 있습니다.

26. 임계영역이란?

더보기

멀티스레드 환경에서 여러 스레드가 동시에 같은 메모리 영역에 접근하게 되면 메모리에 저장된 데이터의 무결성을 훼손할 수 있습니다. 이러한 위험 가능성을 배제하기 위해 반드시 고립성을 보장해야 하는 메모리 영역을 임계영역이라고 합니다. 

27. 직렬화와 역직렬화에 대해 설명하라.

더보기

메모리에 저장되어 있는 데이터를 연속된 바이트 형태로 저장하는 것을 직렬화라고 합니다. 반대로, 연속된 바이트 형태로 저장되어 있던 것을 메모리에 저장하는 것을 역직렬화라고 합니다. 이는 네트워크 패킷에 사용되기도 하며, 세이브 데이터에 사용되기도 합니다. 

28. 프리컴파일헤더란? 장단점은?

더보기

프리컴파일헤더란 컴파일 속도를 높이기 위해 자주 사용되는 헤더를 하나의 헤더파일에 몰아서 선언해놓은 뒤, 이 헤더파일을 미리 컴파일하여 컴파일 타임에 제외하도록 하는 기법입니다.

 

자주 사용되는 헤더파일에 대해 여러 번 컴파일 하지 않아도 되기 때문에 컴파일 타임을 획기적으로 줄일 수 있지만 프리 컴파일 헤더에 포함된 헤더 파일의 내용이 변경될 때마다 결국 새로 컴파일 해야하기 때문에 변경될 가능성이 적은 헤더파일만 포함하는 것이 유리합니다.

29. FSM과 행동트리의 장단점은?

더보기

FSM은 매우 직관적이고 간단하다는 장점이 있습니다. 하지만, 상태의 개수가 늘어나고 상태 간의 관계가 복잡해질수록 가독성이 매우 떨어지고 설계가 복잡해진다는 단점이 있습니다. 이런 이유로, 복잡하지 않은 상태 변화를 구현할 때 용이합니다.

 

행동트리는 하나의 상태를 추가할 때 상태간 전환을 복잡하게 설계할 필요가 없이 특정 행동 노드 뒤에 필요한 노드만 추가하면 된다는 장점이 있습니다. 이로 인해, FSM보다 높은 가독성과 유지보수성을 지니게 됩니다. AI와 같이 복잡한 행동을 설계할 때 용이합니다.

30. 더블버퍼링 기법에 대해 설명하라.

더보기

더블버퍼링이란, 싱글버퍼링에서 발생할 수 있는 플리커링 문제를 해결하기 위한 기법입니다. 그림을 모니터에 출력하는 것과 CPU에서 그리는 작업이 독립되어 있어, 한 개의 버퍼를 사용할 경우 아직 그려지지 않은 버퍼 혹은 지우고 있는 버퍼를 출력하는 경우가 발생할 수 있습니다.

 

이를 해결하기 위해 두개의 버퍼를 사용하는 것이 더블버퍼링입니다. 버퍼에 그리는 작업이 완료되었을 때 모니터에 출력중인 버퍼와 교체하도록 하여 항상 완전히 그려진 버퍼만 모니터에 출력하도록 합니다. 이로 인해, 플리커링 현상을 해결할 수 있습니다.

31. 뮤텍스와 스핀락의 차이

더보기

뮤텍스와 스핀락은 공통적으로 멀티 스레드의 동기화를 달성하기 위한 기법입니다. 두 기법 모두 임계영역을 보호하기 위해, 하나의 스레드에 대해서만 작업을 허용하게 됩니다. 

 

하지만, 뮤텍스의 경우엔 임계영역에 접근하기 위해 대기중인 스레드를 모두 슬립상태로 만들기 때문에 작업 권한을 얻은 이후 컨텍스트 스위칭이 발생하게 됩니다.

 

반면, 스핀락의 경우엔 대기중인 스레드를 무한 루프와 같은 반복문을 통해 작업 상태를 유지하기 때문에, 작업 권한을 얻은 이후에도 컨텍스트 스위칭이 발생하지 않게 됩니다. 하지만, 대기중인 스레드가 계속 작업 상태를 유지하기 때문에 대기 스레드가 CPU를 독점하는 상황이 발생할 수 있습니다. 그렇기 때문에, 대기 시간이 매우 짧은 경우에 한해서 사용하는 것이 좋습니다.

32. 트라이 자료구조에 대해 설명하라.

더보기

문자열을 효율적으로 탐색하기 위한 자료구조입니다. 한 노드에 모든 문자를 배열 형태로 저장한 뒤, 존재하는 문자의 경우 다음 노드와 포인터로 연결하여 문자열을 저장합니다.

 

트리 구조로 되어있기 때문에 문자열의 탐색이 매우 빠르지만, 노드 별로 모든 문자를 배열 형태로 저장하는 탓에 메모리를 많이 사용한다는 단점이 잇습니다.

 

+ Recent posts