๊ณ ์ฑ๋ฅ JVM & Native Image AOT ์ปดํ์ผ๋ฌ ์์ ๊ฐ์ด๋: ๋ค์ดํฐ๋ธ ๋ฐ์ด๋๋ฆฌ ๋น๋(native-image), 0์ด๋ ์ฆ์ ๊ธฐ๋ ๋ฐ ๊ทน์ ๋ฉ๋ชจ๋ฆฌ RSS ์ต์ ํ, ํ์ ์ธ๊ณ ๊ฐ์ (Closed-World)๊ณผ ๋๋ฌ์ฑ ๋ฉํ๋ฐ์ดํฐ(Reachability Metadata / Tracing Agent), Spring Boot 3 AOT ์ปดํ์ผ, Truffle ๋ค๊ตญ์ด ํด๋ฆฌ๊ธ๋(Polyglot) ๋ฐํ์ ๋ฐ PGO ์ฑ๋ฅ ํ๋
## 1. ์ฌ์ ์๊ฑด & ์์คํ
์๊ตฌ์ฌ์ (Prerequisites & Installation)
### ๐ป ์์คํ
์ค์น ๋ฐ ์คํ ์๊ตฌ์ฌ์ (System Requirements)
| ํญ๋ชฉ | ์ต์ ์ฌ์ (Minimum) | ๊ถ์ฅ ์ฌ์ (Recommended) | ์ฐธ๊ณ ๋ฐ ๋น๊ณ |
| :--- | :--- | :--- | :--- |
| **์ด์์ฒด์ (OS)** | Linux (Ubuntu, Debian, RHEL, Alpine), macOS, Windows 10/11 | Ubuntu 22.04 LTS+, macOS Sonoma, Windows 11 (x64) | 64-bit OS ํ์ (x86_64 ๋๋ ARM64 / Apple Silicon) |
| **CPU** | 4 ์ฝ์ด ์ด์ (Native Image ๋น๋ ์) | 8 ์ฝ์ด ~ 16 ์ฝ์ด ์ด์ | AOT ์ ์ ์ฝ๋ ๋ถ์ ๋ฐ ์ต์ ํ ์ปดํ์ผ ์ ๋ชจ๋ ์ฝ์ด ํ๊ฐ๋ |
| **๋ฉ๋ชจ๋ฆฌ (RAM)** | **๋น๋ ํ์: 8 GB ์ด์** / **๋ฐํ์: 32 MB** | **๋น๋ ํ์: 16 GB ~ 32 GB** / **๋ฐํ์: 64 MB** | AOT ๋ถ์ ์ ๋ฉ๋ชจ๋ฆฌ ๋๋ ์๋ชจ, ๋น๋ ํ ์คํ ์ ๊ทน์ ๋ฉ๋ชจ๋ฆฌ ์ ์ |
| **๋์คํฌ (Storage)** | 5 GB ์ด์์ ์ฌ์ ๊ณต๊ฐ | 20 GB ์ด์์ ๊ณ ์ NVMe SSD | GraalVM JDK ๋ฐ C/C++ ๋ค์ดํฐ๋ธ ๋น๋ ์บ์ ๋ณด๊ด |
| **๋น๋ ํด์ฒด์ธ / ๋ฐํ์** | GraalVM JDK 21 LTS ๋๋ JDK 25 | C/C++ ๋ค์ดํฐ๋ธ ์ปดํ์ผ๋ฌ ํด์ฒด์ธ ํ์ | Linux: `gcc`, `glibc-devel`, macOS: `Xcode CLI`, Windows: `MSVC` |
---
### ๐ ๏ธ OS๋ณ C/C++ ๋ค์ดํฐ๋ธ ํด์ฒด์ธ ๋ฐ GraalVM ์ค์น
Native Image๋ JVM ๋ฐ์ดํธ์ฝ๋๋ฅผ OS ์ ์ฉ ๊ธฐ๊ณ์ด๋ก ์ปดํ์ผํ๋ฏ๋ก C ๋ง์ปค ๋ฐ ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ๋ฐ๋์ ์ค์น๋์ด ์์ด์ผ ํฉ๋๋ค.
#### โ OS๋ณ ํ์ ๋ค์ดํฐ๋ธ ๋น๋ ๋๊ตฌ ์ค์น
```bash
# ๐ง Ubuntu / Debian
sudo apt update
sudo apt install -y build-essential libz-dev zlib1g-dev
# ๐ง RHEL / Rocky Linux 9
sudo dnf groupinstall -y "Development Tools"
sudo dnf install -y zlib-devel glibc-devel
# ๐ macOS
xcode-select --install
# ๐ช Windows (๋ฐ๋์ "x64 Native Tools Command Prompt for VS 2022"์์ ์คํ)
# Visual Studio 2022 Community ์ค์น ์ "C++๋ฅผ ์ฌ์ฉํ ๋ฐ์คํฌํฑ ๊ฐ๋ฐ" ์ํฌ๋ก๋ ์ฒดํฌ ํ์
```
#### โก SDKMAN์ผ๋ก ์ต์ GraalVM JDK 21 ์ค์น (๊ฐ์ฅ ์ถ์ฒ)
```bash
# SDKMAN ์ค์น (Linux / macOS)
curl -s "https://get.sdkman.io" | bash
source "$HOME/.sdkman/bin/sdkman-init.sh"
# ์ต์ GraalVM Community Edition JDK 21 ์ค์น ๋ฐ ๊ธฐ๋ณธ๊ฐ ์ค์
sdk install java 21.0.4-graalce
sdk default java 21.0.4-graalce
# ์ค์น ๋ฐ ๋ฒ์ ํ์ธ
java -version
native-image --version
# ์ถ๋ ฅ ์์: native-image 21.0.4 2024-07-16 GraalVM CE Runtime Environment
```
#### โข Docker ๊ณต์ ์ด๋ฏธ์ง ๋น ๋ฅธ ์คํ
```bash
# ๋ค์ดํฐ๋ธ ์ด๋ฏธ์ง ๋น๋ ๊ณต์ ์ปจํ
์ด๋ ํ
docker run -it --rm container-registry.oracle.com/graalvm/native-image:21 bash
```
---
## 2. GraalVM ํต์ฌ ๋ฉํ ๋ชจ๋ธ: JIT vs AOT (Architecture)
**GraalVM**์ ๊ธฐ์กด HotSpot JVM์ ํ๊ณ๋ฅผ ๋ฐ์ด๋๋ ๋ ๊ฐ์ง ํ์ ์ ์ ๊ณตํฉ๋๋ค:
1. **Graal JIT ์ปดํ์ผ๋ฌ**: C2 ์ปดํ์ผ๋ฌ๋ฅผ ๋์ฒดํ์ฌ ํ์ถ ๋ถ์(Escape Analysis)๊ณผ ์ธ๋ผ์ธํ ์ฑ๋ฅ์ ๊ทน๋ํํ ๊ณ ์ฑ๋ฅ JIT ์์ง.
2. **Native Image (AOT)**: ์ ํ๋ฆฌ์ผ์ด์
์คํ ์ ์ ๋ฐ์ดํธ์ฝ๋๋ฅผ ์ ์ ๋ถ์ํ์ฌ **์์ฒด ๊ฒฝ๋ ๋ฐํ์(Substrate VM)**๊ณผ ํจ๊ป ๋จ์ผ OS ์คํ ํ์ผ(๋ฐ์ด๋๋ฆฌ)๋ก ๊ตฝ๋ ๊ธฐ์ .
### ๐ ์คํ ๋ฐฉ์ ๋น๊ต ๋ค์ด์ด๊ทธ๋จ (ASCII Diagram)
```text
[ ์ ํต์ ์ธ HotSpot JVM (JIT ์ปดํ์ผ) ]
Java ์์ค (.java) โโโบ ๋ฐ์ดํธ์ฝ๋ (.class) โโโบ JVM ๊ธฐ๋ (300MB+ ํ ํ ๋น)
โ
โโโ ์ธํฐํ๋ฆฌํฐ ์คํ (๋๋ฆฐ ์ด๊ธฐ ์๋)
โโโ C1 / C2 JIT ์์
(CPU 100% ํ๋ฉฐ ์์ญ์ด ์๋ชจ)
โโโ ์ ์ง์ ์ต์ ํ (๊ธฐ๋ ์๊ฐ 1~3์ด ์์)
[ GraalVM Native Image (AOT ์ปดํ์ผ) ]
Java ์์ค (.java) โโโบ ๋ฐ์ดํธ์ฝ๋ (.class) โโโบ native-image ์ ์ ๋ถ์ (Build Time)
โ
โโโ ํ์ ์ธ๊ณ ๊ฐ์ (Closed-World Assumption)
โโโ ๋ฏธ์ฌ์ฉ ์ฝ๋/๋ฉ์๋ ์์ ์ ๊ฑฐ (DCE)
โโโ Substrate VM (๊ฒฝ๋ GC/์ค๋ ๋) ๋จ์ผ ๊ฒฐํฉ
โผ
[ ๋
๋ฆฝ ์คํ ๋ฐ์ด๋๋ฆฌ (ELF/EXE: 20~40MB) ]
- ๊ธฐ๋ ์๊ฐ: 0.003์ด (3ms!)
- ๋ฉ๋ชจ๋ฆฌ ์ ์ : 20~30MB (์ฆ์ ์๋น ๊ฐ๋ฅ)
```
### ๐ 3๋ฐฉํฅ ๋ฐํ์ ํน์ฑ ๋น๊ต
| ๋น๊ต ํญ๋ชฉ | HotSpot JVM (C2 JIT) | GraalVM JIT (JVM ๋ชจ๋) | GraalVM Native Image (AOT) |
| :--- | :--- | :--- | :--- |
| **๊ธฐ๋ ์๊ฐ (Startup)** | 1์ด ~ ์์ด (ํด๋์ค๋ก๋ฉ/์์
) | 1์ด ~ ์์ด | **0.001์ด ~ 0.05์ด (๋ฐ๋ฆฌ์ด ๋จ์)** |
| **์ด๊ธฐ ๋ฉ๋ชจ๋ฆฌ (RSS)** | 200MB ~ 500MB+ | 200MB ~ 500MB+ | **15MB ~ 50MB (์ด๊ฒฝ๋)** |
| **์ต๊ณ ํผํฌ ์ฒ๋ฆฌ๋** | ๋งค์ฐ ๋์ (๋ฐํ์ ํ๋กํ์ผ๋ง) | **๊ฐ์ฅ ๋์ (๊ณ ๊ธ ํ์ถ ๋ถ์)** | ์ฐ์ (PGO ์ ์ฉ ์ JIT์ ๋๋ฑ) |
| **๋น๋ ์๊ฐ (Build)** | ์์ด (๋งค์ฐ ๋น ๋ฆ) | ์์ด (๋งค์ฐ ๋น ๋ฆ) | **์๋ถ ์์ (CPU/๋ฉ๋ชจ๋ฆฌ ๋๋ ์๋ชจ)** |
| **๋์ ๊ธฐ๋ฅ (Reflection)** | 100% ๋ฌด์ ํ ๋ฐํ์ ํ์ฉ | 100% ๋ฌด์ ํ ๋ฐํ์ ํ์ฉ | **๋๋ฌ์ฑ ๋ฉํ๋ฐ์ดํฐ ์ฌ์ ์ ์ธ ํ์** |
---
## 3. [์ด๋ณด์ 1๋จ๊ณ] 3๋ถ ํฌ๋ก์๋: ๋ค์ดํฐ๋ธ ๋ฐ์ด๋๋ฆฌ ๋น๋ (Hello World)
์ด๋ ํ ํ๋ ์์ํฌ๋ ์์ด ์์ ์๋ฐ ์ฝ๋๋ฅผ ๋จ์ผ ๋
๋ฆฝ ์คํ ํ์ผ๋ก ์ปดํ์ผํ๊ณ ๊ธฐ๋ ์๋๋ฅผ ๋น๊ตํ๋ ์ค์ต์
๋๋ค.
### โ ๋จ์ผ ์๋ฐ ํด๋์ค ์์ฑ (`HelloWorld.java`)
```java
public class HelloWorld {
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
System.out.println("๐ ์๋
, GraalVM Native Image!");
System.out.println("Java ๋ฒ์ : " + System.getProperty("java.version"));
System.out.println("VM ์ด๋ฆ: " + System.getProperty("java.vm.name"));
System.out.println("์คํ ์๋ฃ๊น์ง ์์ ์๊ฐ: " + (System.currentTimeMillis() - startTime) + "ms");
}
}
```
### โก ํ์ค javac ์ปดํ์ผ ๋ฐ ๋ค์ดํฐ๋ธ ์ด๋ฏธ์ง ๋น๋
```bash
# 1. ํ์ค ์๋ฐ ๋ฐ์ดํธ์ฝ๋๋ก ์ปดํ์ผ
javac HelloWorld.java
# 2. native-image ๋ช
๋ น์ด๋ก ๋
๋ฆฝ ์คํ ๋ฐ์ด๋๋ฆฌ ์์ฑ
# -O3 : ์ต๊ณ ์์ค์ ์ต์ ํ ์ ์ฉ
# -o : ์ถ๋ ฅ ์คํ ํ์ผ ์ด๋ฆ ์ง์
native-image -O3 HelloWorld -o hello-native
# ๋น๋ ์๋ฃ ์ hello-native (Linux/macOS) ๋๋ hello-native.exe (Windows) ํ์ผ ์์ฑ๋จ
ls -lh hello-native
# ํ์ผ ํฌ๊ธฐ: ์ฝ 12MB ~ 15MB (์๋ฐ ๋ฐํ์ Substrate VM ํฌํจ)
```
### โข ์คํ ๋ฐ ์๋ ๊ฒ์ฆ (JVM vs Native)
```bash
# [A] ํ์ค JVM ์คํ ์๊ฐ ์ธก์
time java HelloWorld
# ์ถ๋ ฅ:
# ๐ ์๋
, GraalVM Native Image!
# Java ๋ฒ์ : 21.0.4
# VM ์ด๋ฆ: OpenJDK 64-Bit Server VM
# ์คํ ์๋ฃ๊น์ง ์์ ์๊ฐ: 1ms
# real: 0m0.089s (์ฝ 90ms)
# [B] GraalVM Native Image ๋จ๋
๋ฐ์ด๋๋ฆฌ ์คํ (JVM ๋ฏธ์ค์น ํ๊ฒฝ์์๋ ๋์!)
time ./hello-native
# ์ถ๋ ฅ:
# ๐ ์๋
, GraalVM Native Image!
# Java ๋ฒ์ : 21.0.4
# VM ์ด๋ฆ: Substrate VM
# ์คํ ์๋ฃ๊น์ง ์์ ์๊ฐ: 0ms
# real: 0m0.003s (๋จ 3ms! ์ฝ 30๋ฐฐ ๋น ๋ฅธ ์ฆ์ ๊ธฐ๋)
```
---
## 4. [2๋จ๊ณ] ํต์ฌ ๊ธฐ๋ฅ: ํ์ ์ธ๊ณ ๊ฐ์ & ๋๋ฌ์ฑ ๋ฉํ๋ฐ์ดํฐ (Metadata)
Native Image์ ํต์ฌ ์์น์ **ํ์ ์ธ๊ณ ๊ฐ์ (Closed-World Assumption)**์
๋๋ค. ๋น๋ ํ์์ ์ ์ ๋ถ์์ผ๋ก ํธ์ถ ๊ฐ๋ฅ์ฑ์ด ํ์ธ๋ ์ฝ๋๋ง ํฌํจํ๊ณ ๋๋จธ์ง๋ ์ ๊ฑฐํ๋ฏ๋ก, ๋ฐํ์์ ๋์ ์ผ๋ก ๋์ํ๋ **๋ฆฌํ๋ ์
(Reflection)**, **JNI**, **๋ฆฌ์์ค ํ์ผ ๋ก๋ฉ**์ ์ฌ์ ์ ๋ฉํ๋ฐ์ดํฐ๋ก ์๋ ค์ฃผ์ด์ผ ํฉ๋๋ค.
### ๐ต๏ธ Tracing Agent๋ก ๋ฉํ๋ฐ์ดํฐ ์๋ ์ถ์ถ (๊ฐ์ฅ ์ค์)
๋ชจ๋ ๋ฆฌํ๋ ์
์ฝ๋๋ฅผ ์๋ JSON์ผ๋ก ์์ฑํ ํ์ ์์ด, ํ์ค JVM์์ ์ ํ๋ฆฌ์ผ์ด์
์ ํ ๋ฒ ์คํํ๋ฉด์ ์ค์ ๋ก ์ฌ์ฉ๋ ๋์ ํธ์ถ์ ํ์ผ๋ก ์๋ ๋คํํฉ๋๋ค.
```bash
# [1] Native Image Tracing Agent๋ฅผ ๋ถ์ฌ JAR ํ์ผ ์คํ
java -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image \
-jar build/libs/myapp.jar
# [2] ์ ํ๋ฆฌ์ผ์ด์
์ ์ฃผ์ REST API๋ฅผ curl ๋ฑ์ผ๋ก ํธ์ถํ์ฌ ์ฝ๋ ๊ฒฝ๋ก๋ฅผ ๊ณจ๊ณ ๋ฃจ ์คํ
curl http://localhost:8080/api/users
curl http://localhost:8080/api/orders
# [3] ์ ํ๋ฆฌ์ผ์ด์
์ข
๋ฃ ์ META-INF/native-image ๋๋ ํ ๋ฆฌ์ 4๊ฐ JSON ์๋ ์์ฑ:
# โโโ reflect-config.json (๋ฆฌํ๋ ์
๋์ ํด๋์ค ๋ฐ ๋ฉ์๋ ๋ชฉ๋ก)
# โโโ resource-config.json (ํด๋์คํจ์ค์์ ์ฝ์ ํ์ผ ํจํด ๋ชฉ๋ก)
# โโโ proxy-config.json (๋์ ํ๋ก์ ์ธํฐํ์ด์ค ๋ชฉ๋ก)
# โโโ serialization-config.json (์๋ฐ ์ง๋ ฌํ ํด๋์ค ๋ชฉ๋ก)
```
### ๐ `reflect-config.json` ๊ตฌ์กฐ ์์
```json
[
{
"name": "com.example.dto.UserResponse",
"allDeclaredConstructors": true,
"allPublicMethods": true,
"allDeclaredFields": true
}
]
```
---
## 5. [2๋จ๊ณ] ํต์ฌ ๊ธฐ๋ฅ: Spring Boot 3 + GraalVM AOT ๋ค์ดํฐ๋ธ ๋น๋ ์๋ฒฝ ๊ฐ์ด๋
Spring Boot 3.x๋ถํฐ๋ ์ฝ์ด ํ๋ ์์ํฌ ์์ค์์ **Spring AOT(Ahead-of-Time)** ์ฒ๋ฆฌ ์์ง์ด ๋ด์ฅ๋์ด, ๋ฐํ์์ ์ํ๋๋ ๋ฌด๊ฑฐ์ด ์์
๋ค์ ๋น๋ ํ์์ผ๋ก ์๋น๊ฒจ GraalVM Native Image ์์ฑ์ ์๋ฒฝํ๊ฒ ์ง์ํฉ๋๋ค.
### ๐ Spring AOT ์์ง ๋์ ์๋ฆฌ (AOT Processing Lifecycle)
```text
[ 1๋จ๊ณ: Spring Boot ์์ค์ฝ๋ + ๋น๋ ]
โ
โผ (./gradlew nativeCompile ๋๋ ./mvnw -Pnative native:compile)
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Spring AOT Processing Engine (๋น๋ ์์ 1ํ ์คํ) โ
โ โ
โ โ ์ ์ ์ปดํฌ๋ํธ ์ค์บ (Static Component Scanning) โ
โ - ๋ฐํ์ ํด๋์คํจ์ค ํ์ ์์ด, ๋น๋ ํ์์ ๋ชจ๋ @Component, @Service ์ค์บ โ
โ โก ์กฐ๊ฑด๋ถ ๋น(@Conditional) ์ ์ ๊ฒฐ์ โ
โ - ๋น๋ ํ์ ํ๊ฒฝ๋ณ์/ํ๋กํ์ผ์ ๋ฐํ์ผ๋ก ์์ฑํ Bean ์ ์ ๊ณ ์ โ
โ โข AOT ํ๋ก์ ์ฌ์ ์์ฑ (CGLIB ์ ๊ฑฐ โ ์ ์ ์ฝ๋ ์นํ) โ
โ โฃ BeanDefinition ์๋ฐ ์์ค์ฝ๋ ์ ์ ์์ฑ โ
โ - build/generated/aotSources ์ ๋ฆฌํ๋ ์
์๋ ์์ ์ด๊ธฐํ ์ฝ๋ ์์ฑ โ
โ โค Reachability Metadata (๋ฆฌํ๋ ์
/๋ฆฌ์์ค ํํธ) ์๋ ์ง๊ณ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ ์์ฑ๋ AOT ์์ค์ฝ๋ + ๋ฉํ๋ฐ์ดํฐ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ GraalVM native-image Compiler (C/C++ ๋ค์ดํฐ๋ธ ๋ง์ปค) โ
โ โ
โ - ํ์ ์ธ๊ณ ๊ฐ์ (Closed-World) ๊ธฐ๋ฐ ๋ฏธ์ฌ์ฉ ์ฝ๋ ์์ ์ ๊ฑฐ (Dead Code Removal)โ
โ - Substrate VM ๊ฒฝ๋ ๋ฐํ์(GC/Thread) ๋ฐ AOT ์ฝ๋ ๊ธฐ๊ณ์ด ์ปดํ์ผ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
[ ์ด๊ฒฝ๋ ๋จ์ผ OS ์คํ ํ์ผ (๊ธฐ๋: 0.038์ด / ์ด๊ธฐ ๋ฉ๋ชจ๋ฆฌ: 30MB) ]
```
---
### ๐ ๏ธ 2๋ ๋น๋ ๋ฐฉ์ ์๋ฒฝ ๋น๊ต & ํ๋ก์ ํธ ์ค์
Spring Boot ์ ํ๋ฆฌ์ผ์ด์
์ ๋ค์ดํฐ๋ธ ์ด๋ฏธ์ง๋ก ๋ง๋๋ ๋ฐฉ์์ **Native Build Tools(๋ก์ปฌ ๋ฐ์ด๋๋ฆฌ)**์ **Cloud Native Buildpacks(Docker ์ด๋ฏธ์ง)** ๋ ๊ฐ์ง๊ฐ ์์ต๋๋ค.
| ๋น๊ต ํญ๋ชฉ | ๋ฐฉ์ A: Native Build Tools (NBT) | ๋ฐฉ์ B: Cloud Native Buildpacks (Paketo) |
| :--- | :--- | :--- |
| **์์ฑ ๊ฒฐ๊ณผ๋ฌผ** | ํธ์คํธ OS์ฉ ๋จ์ผ ์คํ ํ์ผ (`build/native/...`) | ์ฆ์ ๋ฐฐํฌ ๊ฐ๋ฅํ OCI Docker ์ปจํ
์ด๋ ์ด๋ฏธ์ง |
| **๋ก์ปฌ ํ๊ฒฝ ์๊ตฌ์ฌํญ** | ๋ก์ปฌ์ GraalVM JDK ๋ฐ C/C++ ํด์ฒด์ธ(gcc/MSVC) ํ์ | **Docker ๋ฐ๋ชฌ๋ง ์ผ์ ธ ์์ผ๋ฉด GraalVM ๋ฏธ์ค์น ํ๊ฒฝ๋ ๊ฐ๋ฅ!** |
| **๋น๋ ๋ช
๋ น์ด** | `./gradlew nativeCompile` | `./gradlew bootBuildImage` |
| **์ถ์ฒ ์ค๋ฌด ์๋๋ฆฌ์ค** | ๋ก์ปฌ ๊ฐ๋ฐ ๋๋ฒ๊น
, CLI ๋๊ตฌ, ์ด๊ฒฝ๋ ์๋ฒ๋ฆฌ์ค ๋จ๋
๋ฐ์ด๋๋ฆฌ | **์ฟ ๋ฒ๋คํฐ์ค/ํด๋ผ์ฐ๋ ๋ฐฐํฌ์ฉ ํ์ค ํ๋ก๋์
CI/CD ํ์ดํ๋ผ์ธ** |
---
### โ ๋ฐฉ์ A ์ค์ : Native Build Tools (Gradle & Maven)
#### [Gradle] `build.gradle`
```groovy
plugins {
id 'java'
id 'org.springframework.boot' version '3.3.4'
id 'io.spring.dependency-management' version '1.1.6'
// [1] GraalVM Native Build Tools ๊ณต์ ํ๋ฌ๊ทธ์ธ
id 'org.graalvm.buildtools.native' version '0.10.3'
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
runtimeOnly 'com.h2database:h2'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
// [2] ๋ค์ดํฐ๋ธ ๋น๋ ์ธ๋ถ ์ต์
๊ตฌ์ฑ
graalvmNative {
binaries {
main {
imageName = 'lucky-backend-native'
buildArgs.add('--verbose')
buildArgs.add('--no-fallback')
buildArgs.add('-march=native') // ํ์ฌ CPU ์ ์ฉ ๋ช
๋ น์ด์
(AVX2/AVX-512) ํ์ฉ
buildArgs.add('-J-Xmx8g') // AOT ์ปดํ์ผ๋ฌ ํ๋ก์ธ์ค์ ์ต๋ 8GB ํ ํ ๋น
}
}
// [3] Oracle ๊ณต์ ์คํ์์ค Reachability Metadata ์ ์ฅ์ ์๋ ์ฐธ์กฐ ํ์ฑํ
metadataRepository {
enabled = true
}
}
```
#### [Maven] `pom.xml`
```xml
<build>
<plugins>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<configuration>
<imageName>lucky-backend-native</imageName>
<buildArgs>
<buildArg>--no-fallback</buildArg>
<buildArg>-march=native</buildArg>
</buildArgs>
</configuration>
</plugin>
</plugins>
</build>
```
#### ๋น๋ & ์คํ ๋ช
๋ น์ด
```bash
# ๋ก์ปฌ ํธ์คํธ ๋ค์ดํฐ๋ธ ๋ฐ์ด๋๋ฆฌ ์ปดํ์ผ (์ฝ 2~4๋ถ ์์)
./gradlew nativeCompile
# Maven: ./mvnw -Pnative native:compile
# ์์ฑ๋ ๋
๋ฆฝ ์คํ ํ์ผ ์คํ
./build/native/nativeCompile/lucky-backend-native
```
---
### โก ๋ฐฉ์ B ์ค์ : Cloud Native Buildpacks (Docker ์ด๋ฏธ์ง ์๋ ์์ฑ)
๋ก์ปฌ ๋จธ์ ์ GraalVM์ด๋ gcc๊ฐ ์์ด๋, Docker ๋ฐ๋ชฌ๋ง ์์ผ๋ฉด Paketo ๋น๋ํฉ์ด ๋ด๋ถ ์ปจํ
์ด๋ ์์์ GraalVM์ผ๋ก ์๋ฒฝํ ์ปดํ์ผํด ์ค๋๋ค.
```groovy
// build.gradle (BootBuildImage ํ์คํฌ ํ๋)
tasks.named('bootBuildImage') {
imageName = 'registry.luckytech.com/lucky-app:native-latest'
builder = 'paketobuildpacks/builder-jammy-base:latest'
environment = [
'BP_NATIVE_IMAGE': 'true',
'BP_JVM_VERSION': '21',
'BP_NATIVE_IMAGE_BUILD_ARGUMENTS': '--verbose -march=compatibility'
]
}
```
```bash
# Docker ์ด๋ฏธ์ง ๋น๋ ์คํ (๋ณ๋ Dockerfile ์์ฑ ๋ถํ์!)
./gradlew bootBuildImage
# Maven: ./mvnw spring-boot:build-image
# ๋น๋๋ OCI ์ปจํ
์ด๋ ์ฆ์ ์คํ
docker run --rm -p 8080:8080 registry.luckytech.com/lucky-app:native-latest
```
---
### โข Spring ๋๋ฌ์ฑ ๋ฉํ๋ฐ์ดํฐ & `RuntimeHintsRegistrar` ์๋ฒฝ ๋ง์คํฐ
Spring Boot 3๊ฐ ๋๋ถ๋ถ์ ์คํ๋ง ๋น์ ์๋์ผ๋ก AOT ์ฒ๋ฆฌํ์ง๋ง, **Jackson JSON ์ญ์ง๋ ฌํ DTO**, **๋์ SQL ํ์ผ**, **Thymeleaf ํ
ํ๋ฆฟ**, **์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ**๋ ํํธ๋ฅผ ๋ช
์ํด์ผ ๋ฐํ์ `ClassNotFoundException`์ ๋ฐฉ์งํ ์ ์์ต๋๋ค.
#### ๋ฐฉ๋ฒ 1: ๊ฐ์ฅ ๊ฐ๋จํ DTO ๋ฑ๋ก (`@RegisterReflectionForBinding`)
```java
package com.example.demo.config;
import com.example.demo.dto.OrderRequest;
import com.example.demo.dto.UserResponse;
import org.springframework.aot.hint.annotation.RegisterReflectionForBinding;
import org.springframework.context.annotation.Configuration;
@Configuration
// ํด๋น ํด๋์ค๋ค์ ๋ชจ๋ ์์ฑ์, ๋ฉ์๋, ํ๋ ๋ฆฌํ๋ ์
์ AOT์๊ฒ ์ฌ์ ๋ฑ๋ก
@RegisterReflectionForBinding({OrderRequest.class, UserResponse.class})
public class AotDtoConfig {
}
```
#### ๋ฐฉ๋ฒ 2: ์ ๋ฐ ์ ์ด `RuntimeHintsRegistrar` (๋ฆฌ์์ค, ํ๋ก์, ๋ฆฌํ๋ ์
)
```java
package com.example.demo.config;
import com.example.demo.service.PaymentThirdPartyClient;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportRuntimeHints;
// [1] RuntimeHintsRegistrar ์ธํฐํ์ด์ค ๊ตฌํ
public class AppCustomRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
// A. ๋ฆฌํ๋ ์
ํํธ: ํน์ ํด๋์ค์ public ๋ฉ์๋ ๋ฐ ์์ฑ์๋ง ์ ๋ณ ๋ฑ๋ก
hints.reflection().registerType(PaymentThirdPartyClient.class,
MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,
MemberCategory.INVOKE_PUBLIC_METHODS
);
// B. ๋ฆฌ์์ค ํํธ: ๋ค์ดํฐ๋ธ ๋ฐ์ด๋๋ฆฌ ๋ด๋ถ์ ํฌํจํ ํด๋์คํจ์ค ํ์ผ ํจํด ์ ์ธ
hints.resources().registerPattern("templates/*.html"); // Thymeleaf ๋ทฐ ํ
ํ๋ฆฟ
hints.resources().registerPattern("db/migration/*.sql"); // Flyway / Liquibase ๋ง์ด๊ทธ๋ ์ด์
SQL
hints.resources().registerPattern("banner.txt");
// C. ํ๋ก์ ํํธ: JDK Dynamic Proxy ์ธํฐํ์ด์ค ์ฌ์ ๋ฑ๋ก
hints.proxies().registerJdkProxy(java.sql.Driver.class);
}
}
// [2] ์ค์ ํด๋์ค์ ํํธ ๋ฑ๋ก๊ธฐ ๋ฐ์ธ๋ฉ
@Configuration
@ImportRuntimeHints(AppCustomRuntimeHints.class)
public class AotRegisterConfig {
}
```
---
### โฃ Spring Data JPA & Jackson ํต์ฌ ์ฐ๋ ์ต์ ํ
1. **Java `record`๋ฅผ DTO๋ก ์ฑํ**:
- `record`๋ ์์ฑ์์ getter๊ฐ ๋ช
ํํ ๋ถ๋ณ ๊ฐ์ฒด์ด๋ฏ๋ก, ๋ฆฌํ๋ ์
๊ธฐ๋ฐ ์ธํฐ(Setter) ํ์ ์์ด Jackson์ด ๋ค์ดํฐ๋ธ ํ๊ฒฝ์์ ๊ฐ์ฅ ๋น ๋ฅด๊ณ ์์ ํ๊ฒ ์ง๋ ฌํ/์ญ์ง๋ ฌํํฉ๋๋ค.
2. **CGLIB ํ๋ก์ ์ ๊ฑฐ (`proxyBeanMethods = false`)**:
- `@Configuration(proxyBeanMethods = false)`๋ฅผ ์ค์ ํ๋ฉด Spring์ด ๋ฐํ์ CGLIB ๋ฐ์ดํธ์ฝ๋ ์กฐ์์ ํ์ง ์์ AOT ๋ถ์ ์๋์ ๋ฉ๋ชจ๋ฆฌ ํจ์จ์ด ๊ทน๋ํ๋ฉ๋๋ค.
3. **Hibernate 6 AOT ์ง์**:
- Spring Boot 3 + Hibernate 6์ JPA ์ํฐํฐ์ ๋ํ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์์ฒด์ ์ผ๋ก ์ปดํ์ผํ๋ฏ๋ก, `@Entity` ํด๋์ค๋ ๋ณ๋ ๋ฆฌํ๋ ์
๋ฑ๋ก ์์ด ์๋ฒฝํ ๋์ํฉ๋๋ค.
---
### โค ๋ค์ดํฐ๋ธ ์ฌ๋ผ์ด์ค ํ
์คํธ (`nativeTest`)
๋ค์ดํฐ๋ธ ๋ฐ์ด๋๋ฆฌ๋ก ์ปดํ์ผํ ์ํ์์ ์ค์ ๋จ์/ํตํฉ ํ
์คํธ๋ฅผ ๊ตฌ๋ํ์ฌ, ๋๋ฝ๋ ๋ฆฌํ๋ ์
ํํธ๋ฅผ ๋น๋ ํ์ดํ๋ผ์ธ์์ 100% ์ก์๋
๋๋ค.
```bash
# ๋ค์ดํฐ๋ธ ์ด๋ฏธ์ง ๊ธฐ๋ฐ ํตํฉ ํ
์คํธ ์คํ
./gradlew nativeTest
# Maven: ./mvnw -PnativeTest test
```
> **๊ฒ์ฆ ํฌ์ธํธ**: Mockito ๋ฑ ๋ฐํ์ ๋ฐ์ดํธ์ฝ๋ ์์ฑ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๋ค์ดํฐ๋ธ ํ
์คํธ์์ ์ ์ฝ์ด ์์ผ๋ฏ๋ก, ์์ Spring Boot Slice Test(`@JsonTest`, `@WebMvcTest`) ๋๋ ์ค์ ์ธ๋ฉ๋ชจ๋ฆฌ H2 ํตํฉ ํ
์คํธ๋ฅผ ๊ถ์ฅํฉ๋๋ค.
---
### โฅ ์ค์ ์คํ ๋ฐ ์ฑ๋ฅ ์งํ ๋น๊ต (Before vs After)
```text
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.3.4)
2026-10-10T22:15:00.120Z INFO [ main] c.e.d.DemoApplication : Started DemoApplication in 0.038 seconds (process running for 0.043)
```
| ์ธก์ ์งํ | ์ผ๋ฐ OpenJDK 21 (JVM ๋ชจ๋) | Spring Boot 3 + GraalVM Native | ๊ฐ์ ํจ๊ณผ |
| :--- | :--- | :--- | :--- |
| **๋ถํ
์๋ฃ ์๊ฐ** | **2.15 ์ด** (2,150ms) | **0.038 ์ด (38ms)** | **์ฝ 56๋ฐฐ ์ฆ๊ฐ ๊ธฐ๋** |
| **์ด๊ธฐ ๋ฉ๋ชจ๋ฆฌ (RSS)** | **345 MB** | **31 MB** | **๋ฉ๋ชจ๋ฆฌ ์ ์ 91% ์ ๊ฐ** |
| **์ปจํ
์ด๋ ์ด๋ฏธ์ง ํฌ๊ธฐ** | 380 MB (JVM ํฌํจ) | 35 MB (Distroless) | **๋์คํฌ/๋คํธ์ํฌ 90% ์ ์ฝ** |
| **Kubernetes Pod ๋ฐฐํฌ** | ํธ๋ํฝ ์ค๋น๊น์ง 10์ด+ | **0.1์ด ์ด๋ด ํธ๋ํฝ ์์ ** | **HPA ์คํ ์ค์ผ์ผ๋ง ๊ทน๋ํ** |
---
## 6. [3๋จ๊ณ] ์ค๋ฌด ์์ฉ: Truffle ๋ค๊ตญ์ด ํด๋ฆฌ๊ธ๋ ๋ฐํ์ (Polyglot Programming)
GraalVM์ **Truffle ํ๋ ์์ํฌ**๋ฅผ ์ฌ์ฉํ๋ฉด, ๋จ์ผ JVM ํ๋ก์ธ์ค ์์์ ์๋ฐ ๊ฐ์ฒด๋ฅผ ํ์ด์ฌ(Python)๊ณผ ์๋ฐ์คํฌ๋ฆฝํธ(JavaScript)๋ก ์ง์ ์ ๋ฌํ์ฌ ์ ๋ก ์ค๋ฒํค๋๋ก ์คํํ ์ ์์ต๋๋ค.
### โ ๋น๋ ์์กด์ฑ (`build.gradle`)
```groovy
dependencies {
implementation 'org.graalvm.polyglot:polyglot:24.0.2'
implementation 'org.graalvm.polyglot:js:24.0.2'
implementation 'org.graalvm.polyglot:python:24.0.2'
}
```
---
### โก Java์์ JavaScript & Python ๋์ ํธ์ถ (`PolyglotDemo.java`)
```java
package com.example.demo;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
public class PolyglotDemo {
public static void main(String[] args) {
// [1] JS์ Python ์ธ์ด๋ฅผ ๋ชจ๋ ํ์ฉํ๋ ํด๋ฆฌ๊ธ๋ ์ปจํ
์คํธ ์์ฑ
try (Context context = Context.newBuilder("js", "python")
.allowAllAccess(true)
.build()) {
// [2] JavaScript ์ฝ๋ ์คํ ๋ฐ JSON ํ์ฑ
Value jsResult = context.eval("js",
"const user = { name: 'Lucky', score: 95 };" +
"`${user.name}์ ์ ์๋ ${user.score * 2}์ ์
๋๋ค.`;"
);
System.out.println("JS ์คํ ๊ฒฐ๊ณผ: " + jsResult.asString());
// [3] Python ์ฝ๋ ์คํ ๋ฐ ์ํ ์ฐ์ฐ
Value pyResult = context.eval("python",
"import math\n" +
"def calculate(r):\n" +
" return math.pi * (r ** 2)\n" +
"calculate(5)\n"
);
System.out.println("Python ์คํ ๊ฒฐ๊ณผ (๋ฐ์ง๋ฆ 5 ์์ ๋์ด): " + pyResult.asDouble());
}
}
}
```
---
## 7. [3๋จ๊ณ] ์ค๋ฌด ์์ฉ: ์ด๊ฒฝ๋ Distroless Docker ๋ฐฐํฌ & PGO ํ๋ (Containers & PGO)
### ๐ณ 25MB ์ด๊ฒฝ๋ Distroless ์ปจํ
์ด๋ ๋ฉํฐ์คํ
์ด์ง Dockerfile
๋น๋ ํ๊ฒฝ์๋ GraalVM Native Image ๋๊ตฌ๋ฅผ ์ฐ๊ณ , ์ต์ข
๋ฐํ์ ์ด๋ฏธ์ง์๋ JVM์ด ์ ํ ์๋ ์ด๊ฒฝ๋ Google Distroless ์ ์ ํ๊ฒฝ์ ์ฌ์ฉํ์ฌ ๋ณด์ ์ทจ์ฝ์ ๊ณผ ์ปจํ
์ด๋ ํฌ๊ธฐ๋ฅผ ๊ทน์ ์ผ๋ก ์ค์
๋๋ค.
```dockerfile
# 1๋จ๊ณ: ๋น๋ ์คํ
์ด์ง (GraalVM JDK 21 ํ๊ฒฝ)
FROM container-registry.oracle.com/graalvm/native-image:21 AS builder
WORKDIR /app
# ์์ค์ฝ๋ ๋ฐ Gradle ๋ํผ ๋ณต์ฌ
COPY gradlew settings.gradle build.gradle ./
COPY gradle ./gradle
COPY src ./src
# ๋ค์ดํฐ๋ธ ์ด๋ฏธ์ง ๋น๋ ์คํ
RUN ./gradlew nativeCompile --no-daemon
# 2๋จ๊ณ: ์คํ ์คํ
์ด์ง (JVM์ด ์ ํ ์๋ ์ด๊ฒฝ๋ Distroless Base)
FROM gcr.io/distroless/base-nossl:nonroot
WORKDIR /app
# ๋น๋๋ ๋จ์ผ ๋ฐ์ด๋๋ฆฌ ๋ณต์ฌ
COPY --from=builder /app/build/native/nativeCompile/my-spring-app ./app
# nonroot ๊ณ์ ์คํ ๋ฐ ํฌํธ ๋
ธ์ถ
USER nonroot:nonroot
EXPOSE 8080
ENTRYPOINT ["/app/app"]
```
> **๋ฐฐํฌ ๊ฒฐ๊ณผ**: ์ผ๋ฐ OpenJDK ๊ธฐ๋ฐ ์ด๋ฏธ์ง๊ฐ 400MB~600MB์ ๋ฌํ๋ ๋ฐ๋ฉด, ์ด Docker ์ด๋ฏธ์ง๋ **๋จ 25MB~35MB**๋ก ์์ถ๋์ด ์ฟ ๋ฒ๋คํฐ์ค Pod ๋ฐฐํฌ ๋ฐ ์ค์ผ์ผ์์ ์๋๊ฐ 10๋ฐฐ ์ด์ ๋นจ๋ผ์ง๋๋ค.
---
### ๐๏ธ PGO(Profile-Guided Optimization) ํผํฌ ์ฑ๋ฅ ๊ทน๋ํ
AOT ์ปดํ์ผ์ JIT ์ปดํ์ผ๋ฌ์ฒ๋ผ ๋ฐํ์ ํ๋กํ์ผ๋ง์ ์ค์๊ฐ์ผ๋ก ํ์ง ๋ชปํ๋ฏ๋ก ์ต๊ณ ์ฑ๋ฅ์ด ์ฝ๊ฐ ๋จ์ด์ง ์ ์์ต๋๋ค. ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด **Oracle GraalVM์ PGO** ๊ธฐ๋ฒ์ ์ ์ฉํฉ๋๋ค.
```bash
# 1๋จ๊ณ: ๊ณ์ธก(Instrumentation) ํ๋๊ทธ๋ฅผ ๋ฃ๊ณ ํ๋กํ์ผ ์์ง์ฉ ๋ฐ์ด๋๋ฆฌ ๋น๋
native-image --pgo-instrument -jar myapp.jar -o myapp-instrumented
# 2๋จ๊ณ: ์์ฑ๋ ๋ฐ์ด๋๋ฆฌ๋ฅผ ์คํํ๊ณ ์ค์ ์ด์ ํ๊ฒฝ๊ณผ ์ ์ฌํ ๋ถํ(JMeter/k6) ํ
์คํธ ์ํ
./myapp-instrumented
# ์ ํ๋ฆฌ์ผ์ด์
์ ์ ์ข
๋ฃ ์ default.iprof ํ์ผ ์์ฑ๋จ
# 3๋จ๊ณ: ํ๋กํ์ผ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ํ์ฌ ์ต๊ณ ํผํฌ ์ฑ๋ฅ์ ๋ค์ดํฐ๋ธ ๋ฐ์ด๋๋ฆฌ ์ต์ข
๋น๋
native-image --pgo=default.iprof -jar myapp.jar -o myapp-pgo
```
---
## 8. CLI ํต์ฌ ํ๋๊ทธ ์นํธ์ํธ (`native-image` Flags)
| ํ๋๊ทธ | ์ค๋ช
๋ฐ ์ฃผ ๋ชฉ์ |
| :--- | :--- |
| **`-O3`** | ์ต๊ณ ์์ค์ ์ต์ ํ ์ปดํ์ผ ์ ์ฉ (๊ธฐ๋ณธ๊ฐ: `-O2`) |
| **`--no-fallback`** | ๋ค์ดํฐ๋ธ ์ด๋ฏธ์ง ์คํจ ์ ์ผ๋ฐ JVM fallback jar๋ฅผ ๋ง๋ค์ง ์๊ณ ์ฆ์ ์๋ฌ ์ค๋จ (ํ์ ๊ถ์ฅ) |
| **`-march=native`** | ํ์ฌ ๋น๋ ๋จธ์ ์ CPU ๋ช
๋ น์ด์
(AVX-512 ๋ฑ)์ ๋ง์ถฐ ์ต์ ํ (์ด์์ฑ ์ฃผ์) |
| **`-H:+ReportExceptionStackTraces`** | ๋น๋ ๋์ค ์คํจ ์ ์์ธํ ์์ธ ์คํ ํธ๋ ์ด์ค ์ถ๋ ฅ |
| **`-J-Xmx8g`** | Native Image ๋น๋ ํ๋ก์ธ์ค์ ์ต๋ 8GB ํ ๋ฉ๋ชจ๋ฆฌ ํ ๋น (๋น๋ ์ OOM ๋ฐฉ์ง) |
| **`--initialize-at-build-time`** | ์ง์ ํ ํจํค์ง/ํด๋์ค์ static ์ด๊ธฐํ ๋ธ๋ก์ ๋น๋ ์์ ์ ์คํํ์ฌ ๋ฐํ์ ๋ถํ ์ ๊ฑฐ |
| **`--initialize-at-run-time`** | ๋๋ค ์๋, ๋คํธ์ํฌ ์์ผ ๋ฑ ๋ฐํ์์ ์ด๊ธฐํ๋์ด์ผ ํ๋ ํด๋์ค๋ฅผ ๋ฐํ์ ์์ ์ผ๋ก ์ง์ฐ |
| **`--gc=G1`** | ๋์ฉ๋ ํ ๋ฉ๋ชจ๋ฆฌ ์ฒ๋ฆฌ๋ฅผ ์ํ G1 GC ํ์ฑํ (Oracle GraalVM ์ ์ฉ) |
---
## 9. ์ค๋ฌด ํธ๋ฌ๋ธ์ํ
& ํ๋ก๋์
์ด์ ์ฒดํฌ๋ฆฌ์คํธ (Troubleshooting & Best Practices)
### โ ๏ธ ์ด๋ณด์๊ฐ ๊ฐ์ฅ ํํ ๊ฒช๋ 7๋ ์ค์ ๋ฐ ํด๊ฒฐ์ฑ
| ์ฆ์ ๋ฐ ๋ฌธ์ ์ | ์์ธ ๋ถ์ | ํ์คํ ํด๊ฒฐ ๊ฐ์ด๋ |
| :--- | :--- | :--- |
| **`ClassNotFoundException` ๋๋ `NoSuchMethodException`** | ๋ฆฌํ๋ ์
๋์ ํด๋์ค๊ฐ ํ์ ์ธ๊ณ ๋ถ์์์ ์ ๊ฑฐ๋จ | Tracing Agent๋ฅผ ์คํํ์ฌ `reflect-config.json`์ ์ถ์ถํ๊ฑฐ๋ `@RegisterReflectionForBinding` ํํธ ์ถ๊ฐ |
| **Spring DTO ์ญ์ง๋ ฌํ ์คํจ (`Cannot construct instance... no Creators`)** | Jackson์ด ์์ฑ์๋ฅผ ์ฐพ์ง ๋ชปํด JSON ํ์ฑ ์คํจ | DTO์ `@RegisterReflectionForBinding` ์ถ๊ฐ ๋๋ Java `record`๋ก ์ ์ธํ์ฌ ๋ฆฌํ๋ ์
์์กด์ฑ ํํผ |
| **ํ
ํ๋ฆฟ/์ ์ ํ์ผ 404 (`Resource not found: templates/index.html`)** | AOT ๋น๋๊ฐ ํด๋์คํจ์ค ๋ฆฌ์์ค๋ฅผ ๋ฐ์ด๋๋ฆฌ์ ๋ฏธํฌํจ | `RuntimeHintsRegistrar`์์ `hints.resources().registerPattern("templates/*.html")`๋ก ํจํด ๋ช
์ |
| **๋น๋ ๋์ค `Killed` ๋๋ `Out of Memory Error (Exit Code 137)`** | AOT ์ฝ๋ ์ ์ ๋ถ์ ์ ์์คํ
RAM ๋ถ์กฑ (์ต์ 8GB ํ์) | CI/CD ๋ฌ๋ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ต์ 12GB~16GB๋ก ์ฆ์คํ๊ฑฐ๋ `buildArgs.add("-J-Xmx8g")` ์ถ๊ฐ (Docker ๋น๋ ์ Docker Desktop RAM 8GB+ ํ ๋น) |
| **`@Profile("prod")` ๋๋ `@ConditionalOnProperty`๊ฐ ๋ฐํ์์ ์ ๋ฐ๋** | Spring AOT ์์ง์ ์กฐ๊ฑด๋ถ ๋น ์์ฑ์ **๋น๋ ์์ **์ ๊ฒฐ์ ํจ | ๋น๋ ์์ ์ ํ๋กํ์ผ๊ณผ ๋ฐํ์ ํ๋กํ์ผ์ ์ผ์น์ํค๊ฑฐ๋, ๋ฐํ์ ์กฐ๊ฑด ๋ถ๊ธฐ๋ ๋น ์์ฑ์ด ์๋ ๋ด๋ถ ๋ก์ง `if/else`๋ก ์ฒ๋ฆฌ |
| **`cl.exe not found` ๋๋ `gcc: command not found`** | ํธ์คํธ ์ด์์ฒด์ ์ C/C++ ๋ค์ดํฐ๋ธ ๋น๋ ํด์ฒด์ธ์ด ๋ฏธ์ค์น๋จ | Linux๋ `build-essential`, macOS๋ `Xcode CLI`, Windows๋ `Visual Studio C++ x64 ํ๋กฌํํธ` ์ฌ์ฉ |
| **๋น๋ ํ์ ํด๋์ค ์ด๊ธฐํ ์ถฉ๋ (`Fatal error: java.net.SocketException`)** | ๋คํธ์ํฌ๋ ์ค๋ ๋๋ฅผ ์ฌ๋ ํด๋์ค๊ฐ ๋น๋ ์์ ์ ์ด๊ธฐํ๋จ | `--initialize-at-run-time=com.example.network` ํ๋๊ทธ๋ก ๋ฐํ์ ์ด๊ธฐํ ๊ฐ์ |
---
### ๐ ์๋ฒ๋ฆฌ์ค & ์ฟ ๋ฒ๋คํฐ์ค ํ๋ก๋์
์ฒดํฌ๋ฆฌ์คํธ
1. **AWS Lambda ์ฝ๋ ์คํํธ ์ ๊ฑฐ**:
AWS Lambda Java ํจ์์ ๊ฐ์ฅ ํฐ ์ฝ์ ์ธ 3~5์ด ์ฝ๋ ์คํํธ๋ฅผ GraalVM Custom Runtime(provided.al2023)์ผ๋ก ์ ํํ์ฌ **15ms** ์ด๋ด๋ก ๊ทน๋ณต.
2. **์ฟ ๋ฒ๋คํฐ์ค HPA ์ค์ผ์ผ๋ง ๊ทน๋ํ**:
ํธ๋ํฝ ํญ์ฆ ์ ์ ๊ท Pod๊ฐ ๋จ๋ ์๊ฐ์ 10์ด์์ **0.1์ด**๋ก ๋จ์ถ์์ผ ์ํท ๋ธ๋ ์ด์ปค ํธ๋ํฝ ๋๋กญ ๋ฐฉ์ง.
3. **๊ฐ๋น์ง ์ปฌ๋ ํฐ ์ ํ ์ ๋ต**:
- ๋จ๊ธฐ ์์
/ ์๋ฒ๋ฆฌ์ค / ๋ง์ดํฌ๋ก์๋น์ค (ํ 512MB ์ดํ): ๊ธฐ๋ณธ **Serial GC** ์ ์ง (๋ฉ๋ชจ๋ฆฌ ์ ์ฝ).
- ๋์ฉ๋ ๋ฐ์ดํฐ ์ฒ๋ฆฌ / ๊ณ ํธ๋ํฝ ์น์๋ฒ (ํ 2GB ์ด์): **G1 GC**(`--gc=G1`) ํ์ฑํ ๊ถ์ฅ.
4. **Spring AOT ๋ค์ดํฐ๋ธ ํ
์คํธ ์๋ํ**:
CI/CD ํ์ดํ๋ผ์ธ์ `./gradlew nativeTest`๋ฅผ ํ์๋ก ๋ฐฐ์นํ์ฌ ๋ฐฐํฌ ์ ํํธ ๋๋ฝ ๊ฒ์ฆ.
์๊ฒฌ ๋ฐ ์ง๋ฌธ
0์์ง ๋ฑ๋ก๋ ์๊ฒฌ์ด ์์ต๋๋ค. ์ฒซ ๋ฒ์งธ ๋๊ธ์ ๋จ๊ฒจ๋ณด์ธ์!
๋๊ธ ์์
๋๊ธ ์ญ์