나만의 작은 도서관

[TIL][C++] 250513 MMO 서버 개발 16일차: 포함 디렉터리(Include directory)와 라이브러리 디렉터리(Library directory), Protobuf 파일들이 사용되는 과정 등등… 본문

Today I Learn

[TIL][C++] 250513 MMO 서버 개발 16일차: 포함 디렉터리(Include directory)와 라이브러리 디렉터리(Library directory), Protobuf 파일들이 사용되는 과정 등등…

pledge24 2025. 5. 14. 12:03
주의사항: 해당 글은 일기와 같은 기록용으로, 다듬지 않은 날것 그대로인 글입니다. 

포함 디렉터리(Include directory)와 라이브러리 디렉터리(Library directory)

  • MSVC(Visual Studio)의 설루션에서는 프로젝트의 설정에서 포함 디렉터리와 라이브러리 디렉터리의 경로를 설정할 수 있다.
  • 디렉터리는 윈도에서 “폴더”를 의미한다. 즉 포함 디렉터리는 포함할 폴더, 라이브러리 디렉터리는 라이브러리 폴더가 된다. 각 디렉터리의 경로를 설정하는 이유는 다음과 같다.
    • 포함 디렉터리: 빌드시 #include 지시문을 처리할 때 추가적으로 참조하는 경로를 설정한다. 설정 시 포함 디렉터리 경로에 존재하는 **“헤더 파일(. h)”**을 컴파일러가 참조할 수 있게 된다.
    • 라이브러리 디렉터리: 빌드시 링커가 참조할 “외부 라이브러리 파일(.lib)이” 존재하는 폴더 경로를 추가한다. 설정 시 해당 경로가 라이브러리를 찾는 상대 경로가 된다. 라이브러리를 사용하려면 추가적으로 라이브러리 경로를 설정해야 한다.
    라이브러리 디렉토리 경로: .../Libraries/Libs
    

라이브러리 경로 추가 설정

  • 라이브러리 디렉터리 경로만 설정했다고 자동으로 디렉터리 내에 존재하는 모든 라이브러리를 사용할 수 있게 되는 건 아니다. 따라서, 추가적으로 라이브러리 경로를 설정해야 한다. 방법은 다양하면 대표적인 방법으로 1) 추가 종속성 설정 방법과 2) #pragma comment 사용법 3) 직접 추가 방법이 있다.

라이브러리 경로 설정 예시

Server / Libararies / Libs / ServerCore / Debug / ServerCore.lib
|-라이브러리 디렉토리 경로-||----------라이브러리 경로-----------|
  1. 추가 종속성 설정(. lib 기준)
    • 프로젝트 속성→링커→입력 설정을 찾은 다음, 추가 종속성에 라이브러리 파일명을 추가한다. ex. user32.lib
  2. #pragma comment(. lib 기준)
    • 코드 내에서 아래와 같이 추가한다.
    #pragma comment(lib, "libName")
    
  3. 직접 추가
    • 경로를 알려주는 대신 현재 프로젝트 내부로 이동시키거나 복사해서 넣어주는 방식으로도 사용할 수 있다. 하지만 이 간단한 방식은 프로젝트가 커질수록 적합하지 않으므로 추천하진 않는다.

추가 종속성 설정 vs #pragma comment

  • 추가 종속성 설정 시 장점은 1) 현재 프로젝트 전체에 적용이 되어 의존성 파악이 용이하며, 2) 소스코드와 빌드 설정을 분리할 수 있다는 것이다.
  • 반면, #pragma comment의 장점은 1) 코드만 보고도 사용한 라이브러리를 알 수 있으며, 2) 특정 소스 파일에서만 사용할 수 있다. (#pragma comment는 사용한 소스 파일 내에서만 작동한다.)

Protobuf 파일들이 사용되는 과정

[.proto -> .cpp] => [포함 디렉토리 사용] ==============> [라이브러리 디렉토리 사용]
|----빌드 전----|   |----전처리 단계---| |--컴파일...--| |--------링크 단계--------|
  • 빌드 전:. proto →. cpp로 변환한다. 이때 protoc.exe가 사용되며 protoc 명령어를 입력해 변환한다. 변환 시, xxx.pb.h와 xxx.pb.cc 파일이 생성된다.
  • 전처리 단계: xxx.pb.h와 xxx.pb.cc에서 #include로 사용한 헤더들을 포함 디렉터리를 참조하여 처리한다. 이때 포함 디렉토리 경로에 존재하는 google 폴더가 사용된다.
  • 링크 단계: protobuf 관련 함수 호출 시 이동할, 즉, 정의 부분을 연결한다. 이때 라이브러리 디렉터리 경로 + 라이브러리 경로로 설정한 라이브러리 경로들이 사용되며, libprotobuf.lib나 libprotobufd.lib가 사용된다.

Protobuf 사용법 (일부)

protobuf 객체

  • protobuf는 패킷의 구조인 헤더와 페이로드 중 페이로드의 구조를 정의한다. (이하 편의상 페이로드 구조는 패킷이라 칭함)
  • prptobuf 형식의 패킷을 사용하려면 패킷 클래스가 정의되어 있는 “xxx.pb.h”를 include 해야 한다.
#include "Protocol.pb.h"
  • “xxx.pb.h”를 include 하면 xxx.proto의 메시지(message)들이 class 형태로 정의되어 있어 xxx::msg_name과 같은 객체 형태로 사용할 수 있다.
Protocol::S_TEST pkt; // S_TEST 메세지 타입의 protobuf 객체
  • 각 패킷 클래스들은 굉장히 많은 protobuf 형식의 멤버 함수들이 내장되어 있다. 이를 잘 활용하여 패킷에 데이터를 저장할 수 있다.
int len = pkt.ByteSizeLong(); // 패킷의 크기(payload의 크기)
pkt.SerializeToArray(buffer, len); // pkt 데이터를 직렬화하여 buffer(주소)에 len만큼 넣음.
// result) buffer [[----len 길이----]       ]
  • protobuf 객체를 활용하여 헤더가 달린 온전한 패킷을 만드는 방법은 아래와 같다.
int payloadSize = pkt.ByteSizeLong(); // 패킷의 크기(payload의 크기)
int packetSize = sizeof(PacketHeader) + payloadSize;

// 패킷 헤더를 넣은 송신 버퍼를 준비한다.
SendBufferRef sendBuffer = make_shared<SendBuffer>(packetSize);
PacketHeader* header = reinterpret_cast<PacketHeader*>(sendBuffer->Data());
header->id = pkdId;
header->size = payloadSize;

// 송신 버퍼에서 헤더 뒤에 페이로드를 복사해 넣는다.
pkt.SerializeToArray(sendBuffer + sizeof(PacketHeader), packetSize); // pkt 데이터를 직렬화하여 buffer(주소)에 len만큼 넣음.
// T* arr과 같이 T타입의 배열로 버퍼를 바라보면 아래와 같이 사용가능.
// pkt.SerializeToArray(&header[1], packetSize); // pkt 데이터를 직렬화하여 buffer(주소)에 len만큼 넣음.

// 결과 => 송신 버퍼(sendBuffer)에 온전한 패킷 저장됨.

메모리 풀링에서 Open-Close의 개념

  • 메모리 풀링 시 open-close의 개념이 존재한다. 우선 메모리 풀링이란, 메모리 할당 및 해제 횟수를 줄이기 위해 한 번에 큰 메모리를 할당한 다음, 필요할 때마다 조금씩 떼어서 쓰고 반납하는 방식을 말한다.
  • 이러한 메모리 풀링에서 open-close는 아래와 같은 의미를 지닌다.
    • open: 새로운 메모리를 사용. 지정한 메모리 크기만큼 사용 가능한 메모리(free한 메모리)의 쓰기 작업을 허용하도록 전환한다.
    • close: 사용한 메모리를 반납. 해당 메모리는 free하지도, 쓰기 작업을 허용하지도 않는다.
// 현재 MemoryChunk 상황 ('---'는 사용함, '____'는 free한 메모리)
[[----------]_________________________]

// Open(10): 10바이트를 추가 사용. ('XXX'는 열린 메모리)
// 일반적으로 메모리 시작 주소를 반환.
[[----------][XXXXXXXXXX]_____________]

// Close(): 사용한 메모리를 반납.
[[----------------------]_____________]

XCOPY /Y Enum.pb.h “../../../GameServer”의 의미

  • XCOPY: 파일 복사 명령어
  • /Y: 이미 파일이 존재해도 덮어쓰는 옵션 추가
  • Enum.pb.h: 복사 대상 파일의 경로
  • “../../../GameServer”: 복사한 파일을 둘 경로

⇒ 최종 의미: “현재 위치(배치 파일이 있는 폴더 내부)에 존재하는 Enum.pb.h 파일을 3칸 위 폴더에 있는 GameServer 폴더 안에 복사해 넣는다. 만약 이미 존재한다면 덮어쓴다(/Y 옵션)”

pushd %~dp0

  • 뜻: 현재 실행 중인 배치 파일이 위치한 디렉터리로 작업 디렉터리를 변경해라.
  • pushd(=push directory): 디렉토리 스택에 뒤에 나올 경로를 push해라
  • %~dp0(= drive path): 이 명령어를 실행하는 위치(명령어를 실행하는 배치 파일의 위치)의 드라이브 경로
drive => ex. "C:"
path => Server/Gameserver/.../xxx.bat
0 => 이 명령어를 실행하는 위치
  • 이 코드를 배치 파일 가장 위에 작성하면, 원격으로 배치 파일을 실행해도 배치 파일과 같은 폴더 내에 있는 protoc를 항상 찾을 수 있음.