Docker 컨테이너
한 줄 요약
텍스처 변환(JPEG → KTX2) 로직을 Docker 컨테이너로 패키징하여, EC2 인스턴스에서 일관된 환경으로 실행한다. node:20-slim 기반에 gltf-transform CLI와 KTX-Software를 포함하며, CPU 코어 수만큼 병렬 처리를 수행한다.
왜 Docker가 필요했는가
텍스처 변환 파이프라인은 여러 도구의 조합으로 동작한다:
- gltf-transform CLI (v4): glTF/glb 파일의 텍스처를 KTX2로 변환하는 Node.js 기반 도구
- KTX-Software (4.4.2): Khronos Group이 관리하는 공식 KTX2 인코더/디코더. 네이티브 바이너리.
이 도구들은 Node.js 런타임, 네이티브 바이너리, 특정 버전의 라이브러리 등 실행 환경에 대한 의존성이 있다. EC2 인스턴스에 직접 설치하면 "이 서버에서는 되는데 저 서버에서는 안 된다"는 환경 불일치 문제가 발생할 수 있다.
Docker는 이 문제를 "실행 환경 자체를 패키징"하여 해결한다. 필요한 모든 도구와 의존성을 하나의 컨테이너 이미지에 담아두면, 어떤 EC2 인스턴스에서든 동일한 환경에서 실행된다. 또한 변환 작업이 끝나면 EC2가 종료되고 다시 시작될 때도, 이미지만 있으면 환경 설정 없이 바로 실행 가능하다는 점이 Lambda → EC2 트리거 구조와 잘 맞았다.
컨테이너 구성
베이스 이미지: node:20-slim
+ gltf-transform CLI v4 (npm)
+ KTX-Software 4.4.2 (네이티브 바이너리)node:20-slim을 베이스로 선택한 이유는, gltf-transform이 Node.js 기반이므로 Node.js 런타임이 필수이고, slim 변형은 불필요한 패키지를 제거하여 이미지 크기를 최소화하기 때문이다.
Docker의 핵심 개념
이 프로젝트를 계기로 Docker를 학습하면서 이해한 핵심 개념들이다.
이미지와 컨테이너의 차이
이미지(Image)는 "실행 환경의 스냅샷"이다. Dockerfile에 정의된 OS, 런타임, 라이브러리, 코드가 모두 포함된 읽기 전용 패키지다. 컨테이너(Container)는 이 이미지를 기반으로 실제로 실행 중인 인스턴스다. 하나의 이미지로 여러 컨테이너를 동시에 실행할 수 있다.
비유하면, 이미지는 "설치 CD"이고 컨테이너는 "그 CD로 설치하여 실행 중인 프로그램"이다.
레이어 구조
Docker 이미지는 레이어(layer)로 구성된다. Dockerfile의 각 명령어(FROM, RUN, COPY 등)가 하나의 레이어를 생성한다. 변경된 레이어만 다시 빌드하므로, 자주 변하지 않는 것(OS, 런타임)을 먼저 배치하고 자주 변하는 것(소스 코드)을 나중에 배치하면 빌드 시간을 줄일 수 있다.
이 프로젝트에서는 node:20-slim(잘 안 변함) → KTX-Software 설치(잘 안 변함) → gltf-transform 설치(가끔 변함) → 변환 스크립트(자주 변함) 순서로 레이어를 구성하여 캐시 효율을 높였다.
slim 이미지 선택
Docker Hub에서 같은 Node.js 이미지도 여러 변형이 존재한다:
node:20— 전체 Debian OS 포함 (~900MB)node:20-slim— 최소 Debian 패키지만 포함 (~200MB)node:20-alpine— Alpine Linux 기반, 가장 가벼움 (~130MB)
node:20-slim을 선택한 이유는, KTX-Software의 네이티브 바이너리가 glibc에 의존하는데, Alpine은 musl libc를 사용하여 호환성 문제가 발생할 수 있기 때문이다. slim은 glibc 호환성을 유지하면서도 불필요한 패키지를 제거하여 크기와 호환성의 균형을 맞춘다.
환경변수를 통한 설정 주입
컨테이너는 환경변수(Environment Variable)로 외부와 소통한다. 이미지 안에 S3 경로나 AWS 인증 정보를 하드코딩하면 보안 문제가 생기고 재사용성이 떨어진다. 대신 컨테이너 실행 시 환경변수로 주입하면, 같은 이미지로 다른 프로젝트, 다른 S3 경로의 작업을 처리할 수 있다.
더 깊은 공부가 필요한 영역
Docker를 실무에 적용하는 데 성공했지만, 아직 깊이 있게 이해하지 못한 영역들이 있다:
- 멀티스테이지 빌드: 빌드 단계와 실행 단계를 분리하여 최종 이미지 크기를 더 줄이는 기법
- 보안 설정: non-root 사용자 실행, 이미지 취약점 스캔 등
- 네트워크와 볼륨: 컨테이너 간 통신, 호스트 파일시스템 마운트의 심화 활용
- 오케스트레이션: Docker Compose, Kubernetes 같은 다중 컨테이너 관리 도구
이 영역들은 현재의 단일 컨테이너 구조에서는 당장 필요하지 않았지만, 파이프라인이 복잡해지거나 여러 서비스를 연동해야 할 때 필요해질 것이다.
프론트엔드 개발자가 인프라까지 확장한 경험
이 컨테이너 구축은 Docker를 처음으로 실무에 적용한 경험이었다. 위에서 정리한 개념들을 학습하면서 구축을 진행했고, 동작하는 시스템을 만들 수 있었다. 그러나 깊은 이해가 아직 부족한 영역이 있다는 것을 인식하고 있으며, 이는 지속적으로 보완해야 할 부분이다.
이 경험에서 중요한 것은 Docker 자체의 깊은 이해가 아니라, "프론트엔드 개발자가 필요에 의해 인프라 영역까지 확장할 수 있다"는 사실이다. 텍스처 최적화라는 프론트엔드 성능 문제를 해결하기 위해, 변환 파이프라인 → EC2 트리거 → Docker 컨테이너까지 직접 구축한 것이다.
브라우저 안의 성능 문제(메모리, GPU)를 분석하고 → 브라우저 밖의 데이터 파이프라인까지 해결한다는 것이, "브라우저 밖 성능 병목까지 직접 해결할 줄 아는 프론트엔드 엔지니어"라는 포지셔닝의 실체다.
이 경험에서 추출한 원칙
-
"환경을 코드로 관리한다"가 Docker의 본질이다. "이 서버에 ssh로 접속해서 npm install하고, 이 바이너리를 다운받아서..."라는 수동 설정 대신, Dockerfile 하나에 모든 환경 설정이 선언적으로 정의된다. 서버가 바뀌어도, 팀원이 바뀌어도 동일한 환경이 재현된다.
-
프론트엔드 성능 최적화의 끝은 서버 파이프라인일 수 있다. GPU 메모리를 69% 줄인 KTX2 변환은 브라우저 밖에서 일어난다. 프론트엔드 개발자가 "내 영역은 브라우저까지"라고 선을 그으면, 정작 가장 큰 성능 개선 기회를 놓칠 수 있다.
Docker 컨테이너
인프라·배포