DLL은 별도의 파일로 존재하며 프로그램 실행 중에 필요할 때 메모리에 로드된다.
여러 프로그램이 동일한 DLL을 공유할 수 있어 시스템 메모리 사용량을 크게 줄일 수 있다.
예를 들어, Windows의 kernel32.dll은 거의 모든 프로그램이 사용하지만 메모리에는 한 번만 로드된다.
또한, 애플리케이션을 기능별로 분할하여 유지보수성을 높인다.
플러그인 시스템이나 확장 가능한 아키텍처 구현에 거의 필수적으로 사용된다.
실제로 상용엔진(unity, unreal)의 폴더를 가보면, DLL들이 어마무시하게 많이 있다.
기능별로 따로 빼두고, 이를 불러오는 방식으로 사용하는 게 아무래도 한 공간에 모두 구현하는 방식보다 관리하기 편하기도 하기 때문이다.
DLL 프로젝트 만들기
DLL로 사용할 새 프로젝트를 생성한다. (빈 프로젝트로 생성함)

먼저, DLL 프로젝트의 설정으로 가서 컴파일 결과물 경로를 수정한다.
그리고, 이 프로젝트는 DLL로 사용할 것이기 때문에 Configuration Type을 .dll로 설정한다.


헤더 파일과 cpp파일을 추가한다.

DLL에서 함수나 클래스를 만들 때, 이를 DLL 외부에서도 사용할 수 있도록 내보내기 위해서는 __declspec(dllexport) 한정자를 붙여줘야 한다.
이는 DLL에서 함수 또는 변수를 내보내도록 컴파일러 및 링커에 알려서 다른 애플리케이션에서 사용할 수 있도록 한다.
// CustomMath.h
#pragma once
int __declspec(dllexport) Add(int a, int b);
함수의 선언부에서 __declspec(dllexport)를 붙여줬다면, 구현부에서는 생략해도 괜찮다.
// CustomMath.cpp
#include "CustomMath.h"
int Add(int a, int b)
{
return a + b;
}
위와 같이 설정 후, DLL 프로젝트를 빌드해 보면 다음과 같은 경로에 .dll 파일과 .lib 파일이 생긴다.
dll 파일은, 런타임에 메모리로 로드되어 여러 프로그램이 공유할 수 있는 실행 가능한 코드 컨테이너로, 이게 있어야 DLL을 사용하는 프로젝트의 빌드가 가능하다.
lib 파일은, 컴파일 시점에 실행 파일에 완전히 통합되는 미리 컴파일된 객체 코드 아카이브로, 이게 있어야 DLL을 사용하는 프로젝트에서 DLL의 코드를 읽을 수 있다.

참고로, DLL은 main 함수가 없어도 빌드에 문제가 없다.
DLL 매크로 자동화
앞서, DLL에서 만든 함수나 클래스를 외부로 내보내기 위해서는 __declspec(dllexport) 한정자를 붙여야 한다고 했다.
내보낸 거를 DLL이 아닌, 다른 프로젝트에서 사용하려면 __declspec(dllimport) 한정자를 붙여줘야 한다.
이 한정자를 매크로를 통해 관리해 보자.
DLL 프로젝트에 새 폴더를 하나 추가한다.
이름은 Core로 짓고, CustomMath 파일들을 Core 폴더 안으로 넣는다.


새 Core.h 헤더 파일을 생성하고, 다음 코드를 작성한다.
이는, BUILDENGINEDLL이 0이면 4번 줄의 코드가 활성화되고, 1이면 7번 줄의 코드가 활성화된다는 의미이다.

근데 BUILDENGINEDLL이 뭐냐? 이건 그냥 이 코드를 위해 임의로 만든 문자다.
아무런 정의 없이 사용하게 되면 기본적으로 0의 값이 들어간다.
따라서, DLL 프로젝트에서 이것을 정의하고 사용하게 되면, __declspec(dllexport) 한정자가 붙는다.
나머지 프로젝트는 따로 설정을 안 하면 모두 0으로 정의되어, __declspec(dllimport) 한정자가 붙게 된다.
이제 DLL 프로젝트에서 BUILDENGINEDLL을 1로 설정해 보자.
Engine의 프로젝트 속성에서, C/C++의 Preprocessor로 가서, Preprocessor Definitions에 BUILDENGINEDLL; 구문을 추가한다.
이는, Engine 프로젝트 전처리기에서 BUILDENGINEDLL라는 것을 정의한다는 의미이다.

이렇게 설정해 주면, 이제 Engine 프로젝트에서는 #define ENGINE_API __declspec(dllexport) 구문이 활성화되는 것을 확인할 수 있다.

이제, DLL로 내보낼 함수나 클래스에 모두 이 매크로를 사용해 주면 된다.

추가로, Core 폴더가 아닌 다른 폴더에서 Core.h를 include 할 수 있도록, 헤더 경로를 수정한다.
$(ProjectDir)\;$(ProjectDir)\Core\
Core.h가 들어있는 폴더만 특별히 절대 경로로 지정해 주고, 나머지는 include에 폴더 이름까지는 포함해야 한다.

ex) Core.h는 그냥 include 해도 되지만, Core 폴더 외의 폴더에 있는 헤더들은 따로 경로를 더 추가해줘야 함.
#include "Core"
#include "Math\Vector2.h"
헤더 및 lib, dll 경로 자동화
아직 만들지는 않았지만, DLL을 사용하는 프로젝트에서는 DLL 프로젝트의 헤더 파일과 lib, dll 파일들이 모두 필요하다.
이를 DLL 프로젝트에서 정리하여 관리해 주면, DLL을 사용하는 프로젝트에서 더 편하게 파일들을 가져올 수 있다.
솔루션 경로에 헤더들을 모아둘 Includes 폴더를 생성한다.
lib, dll 파일들을 모아둘 Library 폴더를 생성한다.

먼저, DLL 프로젝트의 헤더 파일들이 Includes에 저장되도록 자동화 설정을 해보자.
Engine 속성의 Build Events의 Pre-Build Event로 가서, 다음 코드를 넣는다.
Command Line에 xcopy *.h $(SolutionDir)\Includes\ /e /y
이는, 현재 프로젝트 경로에 모든 .h 파일들을, Includes 폴더로 복사하라는 의미이다.
/e는 하위 폴더까지 포함한다는 의미고, /y는 이미 파일이 있으면 덮어씌우라는 의미이다.

Engine 프로젝트를 빌드해 보면, Includes\Core 폴더에 헤더 파일들이 모두 복사되어 저장된 것을 확인할 수 있다.

다음으로, lib 파일과 dll 파일을 Library 폴더에 저장해 보자.
Engine 속성의 Build Events의 Post-Build Event로 가서, 다음 코드를 넣는다.
xcopy $(OutDir)\*.lib $(SolutionDir)\Library\Engine\ /y
xcopy $(OutDir)\*.dll $(SolutionDir)\Library\Engine\ /y
출력 경로에 있는 lib 파일과 dll 파일을 Library로 복사하라는 의미이다.


Engine 프로젝트를 빌드해 보면, Library\Engine 폴더에 파일들이 모두 복사되어 저장된 것을 확인할 수 있다.

DLL을 사용하는 프로젝트 만들기
DLL을 사용할 새 프로젝트를 생성한다. (빈 프로젝트로 생성함)
기본 Main.cpp 파일을 생성한다.

프로젝트의 설정으로 가서 컴파일 결과물 경로를 수정한다. (DLL 프로젝트와 동일하게 설정함)

헤더 파일 경로 설정
다음으로, DLL의 헤더 파일을 추가해 보자.
사실 그냥 추가할 헤더 파일의 경로를 그대로 포함하면 되긴 된다.
// Game.cpp
#include <iostream>
#include "..\Includes\Core\CustomMath.h"
int main()
{
std::cout << Add(10, 20) << "\n";
}
하지만 이러면 보기 불편하니, 헤더 파일 경로를 추가해 보자.
앞서, DLL의 프로젝트를 설정할 때, __declspec(dllimport)를 구별하도록 설정하고, 헤더 파일을 따로 Includes 폴더에 복사해 저장하는 설정을 했다. 복사된 헤더들은 __declspec(dllimport)으로 설정된다.
Game 프로젝트의 설정으로 가서, C/C++ -> General -> Additional Include Directories에 Includes 폴더 경로를 추가한다.
예시에서는 핵심 기능들을 Core 폴더에 모아둔다는 가정이 있다.
따라서, ..\Includes\;와 ..\Includes\Core\;을 둘 다 넣었다.

lib 및 dll 경로 설정
마지막으로, lib와 dll의 경로를 설정해 주면 된다.
먼저, lib의 종속성을 추가해 보자.
이 종속성은 포함하려는 DLL 프로젝트의 lib를 추가하는 것이다.
이는 두 가지 방법이 있다.
첫 번째는 프로젝트 속성의 Linker->Input->Additional Dependencies에 .lib 파일 경로를 추가해 주는 것이다.

두 번째는, 사용하려는 프로젝트에 #pragma comment(lib, "라이브러리 이름")을 추가하는 것이다.
// Game.cpp
#include <iostream>
#include "CustomMath.h"
#pragma comment(lib, "Engine.lib")
int main()
{
std::cout << Add(10, 20) << "\n";
}
그리고, lib 파일의 경로를 탐색하기 위해서, 경로를 설정해줘야 한다.
프로젝트 설정 -> Linker -> General -> Additional Library Directories에 lib가 저장되는 경로를 지정한다.
앞서, DLL 프로젝트 설정에서 lib와 dll 파일이 모두 Library 폴더로 복사되도록 설정했었다.

다음으로, DLL을 사용하기 위해서 추가 라이브러리 디렉터리에서 dll 파일 경로를 지정해줘야 한다.
프로젝트 설정 -> Build Events -> Post-Build Event -> Command Line에 커맨드를 추가한다.
xcopy $(SolutionDir)\Library\Engine\*.dll $(OutDir)\ /y
Library 폴더에 있는 dll 파일을 프로젝트의 결과물(exe파일이 생성되는 곳)에 복사한다.

코드를 실행해 보면, DLL을 잘 가져와서 함수가 동작한다.
// Game.cpp
#include <iostream>
#include "CustomMath.h"
int main()
{
std::cout << Add(10, 20) << "\n";
}

'📕Programming > 📝C/C++' 카테고리의 다른 글
| [C / C++] RTTI (0) | 2025.07.23 |
|---|---|
| [C / C++] 스택 & 힙 메모리 할당 과정 (0) | 2025.07.20 |
| [C / C++] 정적 라이브러리 (static library) (0) | 2025.07.18 |
| [C / C++] 메모리 누수 찾기 (0) | 2025.07.16 |
| [C / C++] C++ 동작 방식 (0) | 2025.07.15 |