프론트엔드 개발자가 Docker를 알아야 하는 이유
Keycloak 로컬 테스트하면서 시작한 Docker 입문기
프론트엔드 개발자인데 Docker를 알아야 할까요?
솔직히 저도 그런 생각이었습니다.
"그거 백엔드나 인프라 담당이 알아야 하는 거 아닌가?"
그런데 최근 Keycloak 인증을 연동하면서 생각이 바뀌었습니다.
회사에서 Keycloak 기반 인증을 적용하게 됐는데, 문제는 개발 서버가 사내 Wi-Fi에서만 접근 가능하다는 것이었습니다.
집에서 인증 플로우를 테스트해보고 싶은데 방법이 없었습니다.
그래서 직접 Keycloak을 로컬에 띄워보기로 했습니다.
플로우 확인도 할 겸, 이참에 제대로 이해해보자 싶었습니다.
이번 글에서는 프론트엔드 개발자 관점에서 Docker를 이해하고,
실제로 로컬 개발 환경에서 활용하는 방법을 정리해봅니다.
Docker가 뭔데?
Docker는 컨테이너 기반 가상화 플랫폼입니다.
"가상화"라고 하면 VMware 같은 가상 머신(VM)이 떠오를 수 있는데,
Docker는 그것보다 훨씬 가볍습니다.
가상 머신 vs 컨테이너
가상 머신 (VM)
┌─────────────────────────────┐
│ Application │
├─────────────────────────────┤
│ Guest OS (전체) │ ← Windows/Linux 전체 설치
├─────────────────────────────┤
│ Hypervisor │
├─────────────────────────────┤
│ Host OS │
└─────────────────────────────┘
VM은 완전한 운영체제를 통째로 설치합니다.
그래서 무겁고 느립니다. 시작하는 데 몇 분씩 걸리기도 합니다.
Docker 컨테이너
┌─────────────────────────────┐
│ Application │
├─────────────────────────────┤
│ Container Runtime │ ← 필요한 것만 포함
├─────────────────────────────┤
│ Docker Engine │
├─────────────────────────────┤
│ Host OS │
└─────────────────────────────┘
Docker는 호스트 OS의 커널을 공유합니다.
필요한 라이브러리와 애플리케이션만 패키징하기 때문에 가볍고 빠릅니다.
컨테이너 시작이 몇 초면 됩니다.
비유하자면, VM은 집을 통째로 짓는 것이고,
Docker는 컨테이너 박스를 쌓는 것입니다.
박스 안에 필요한 것만 넣으면 되니까 훨씬 효율적입니다.
왜 프론트엔드 개발자에게 필요한가
- 로컬 개발 환경 구축: 백엔드 API, 인증 서버, DB 등을 로컬에서 실행
- 일관된 환경: "내 컴퓨터에서는 되는데" 문제 해결
- 빠른 셋업: 새 프로젝트 시작할 때 복잡한 설치 과정 생략
- 테스트 환경: E2E 테스트용 서버 구축
저 같은 경우 Keycloak 연동 테스트할 때 Docker가 없었으면
실제 개발 서버에 계속 배포하면서 테스트해야 했을 겁니다.
그게 얼마나 비효율적인지는 경험해보신 분들은 아실 거예요.
핵심 개념
이미지 (Image)
이미지는 컨테이너를 만들기 위한 템플릿입니다.
프로그램 설치 파일이라고 생각하면 됩니다.
이미지 자체는 실행되지 않고, 이걸 기반으로 컨테이너를 만들어서 실행합니다.
# Docker Hub에서 이미지 다운로드
docker pull nginx
# 로컬에 있는 이미지 목록
docker images이미지는 계층(Layer) 구조로 되어 있습니다.
기본 OS 레이어 위에 필요한 패키지를 쌓아 올리는 방식입니다.
컨테이너 (Container)
컨테이너는 이미지를 실행한 인스턴스입니다.
이미지가 설치 파일이라면, 컨테이너는 실행 중인 프로그램입니다.
하나의 이미지로 여러 컨테이너를 만들 수 있습니다.
# 컨테이너 실행
docker run nginx
# 실행 중인 컨테이너 목록
docker ps
# 모든 컨테이너 (중지된 것 포함)
docker ps -aDocker Hub
Docker Hub는 이미지 저장소입니다.
npm이 JavaScript 패키지 저장소인 것처럼, Docker Hub는 Docker 이미지 저장소입니다.
공식 이미지들이 많이 등록되어 있어서,
대부분의 소프트웨어는 docker pull로 바로 받아서 쓸 수 있습니다.
# PostgreSQL 이미지 다운로드
docker pull postgres
# Node.js 이미지 다운로드
docker pull node:20기본 명령어
Docker CLI는 처음에 복잡해 보이지만,
자주 쓰는 명령어는 몇 개 안 됩니다.
이미지 관련
# 이미지 다운로드
docker pull [이미지명]:[태그]
docker pull node:20-alpine
# 이미지 목록 확인
docker images
# 이미지 삭제
docker rmi [이미지명]태그는 버전을 의미합니다.
생략하면 latest가 기본값입니다.
alpine은 경량화된 Linux 배포판 기반 이미지입니다.
용량이 작아서 자주 사용됩니다.
컨테이너 실행
# 기본 실행
docker run [이미지명]
# 백그라운드 실행 (-d: detached)
docker run -d nginx
# 포트 매핑 (-p: port)
docker run -d -p 8080:80 nginx
# 호스트의 8080 포트 → 컨테이너의 80 포트
# 이름 지정 (--name)
docker run -d --name my-nginx -p 8080:80 nginx
# 환경 변수 설정 (-e: environment)
docker run -d -e POSTGRES_PASSWORD=secret postgres포트 매핑이 처음에 헷갈렸는데,
-p 호스트포트:컨테이너포트 순서입니다.
localhost:8080으로 접속하면 컨테이너의 80 포트로 연결됩니다.
컨테이너 관리
# 실행 중인 컨테이너 목록
docker ps
# 모든 컨테이너 목록 (중지된 것 포함)
docker ps -a
# 컨테이너 중지
docker stop [컨테이너명/ID]
# 컨테이너 시작 (중지된 컨테이너)
docker start [컨테이너명/ID]
# 컨테이너 삭제
docker rm [컨테이너명/ID]
# 실행 중인 컨테이너 삭제 (강제)
docker rm -f [컨테이너명/ID]로그와 디버깅
# 컨테이너 로그 확인
docker logs [컨테이너명/ID]
# 실시간 로그 (follow)
docker logs -f [컨테이너명/ID]
# 컨테이너 내부 접속
docker exec -it [컨테이너명/ID] /bin/bash
# 또는
docker exec -it [컨테이너명/ID] shdocker exec -it로 컨테이너 내부에 들어가서 직접 확인할 수 있습니다.
디버깅할 때 유용합니다.
Keycloak 로컬 실행 예시
제가 실제로 Keycloak을 로컬에서 테스트할 때 사용한 방법입니다.
기본 실행
docker run -d \
--name keycloak \
-p 8080:8080 \
-e KEYCLOAK_ADMIN=admin \
-e KEYCLOAK_ADMIN_PASSWORD=admin \
quay.io/keycloak/keycloak:latest \
start-dev명령어가 좀 길어 보이지만, 하나씩 보면 간단합니다.
-d: 백그라운드 실행--name keycloak: 컨테이너 이름-p 8080:8080: 포트 매핑-e KEYCLOAK_ADMIN=admin: 관리자 계정 설정quay.io/keycloak/keycloak:latest: 이미지 (Keycloak은 Docker Hub 대신 quay.io 사용)start-dev: 개발 모드로 시작
실행 후 http://localhost:8080 으로 접속하면 Keycloak 화면이 뜹니다.
start-dev는 개발용 모드입니다.
프로덕션에서는 절대 사용하면 안 됩니다.
HTTPS, 데이터베이스 설정 등이 필요합니다.
데이터 유지하기 (Volume)
위 방식으로 실행하면 컨테이너를 삭제할 때 설정한 데이터도 함께 사라집니다.
Realm 설정, 사용자 정보 등을 유지하려면 볼륨을 사용해야 합니다.
docker run -d \
--name keycloak \
-p 8080:8080 \
-e KEYCLOAK_ADMIN=admin \
-e KEYCLOAK_ADMIN_PASSWORD=admin \
-v keycloak_data:/opt/keycloak/data \
quay.io/keycloak/keycloak:latest \
start-dev-v keycloak_data:/opt/keycloak/data로 볼륨을 연결하면,
컨테이너를 삭제하고 다시 만들어도 데이터가 유지됩니다.
처음에 이거 몰라서 Realm 설정을 몇 번이나 다시 했습니다.
Docker Compose
여러 컨테이너를 함께 실행해야 할 때 Docker Compose를 사용합니다.
예를 들어 Keycloak + PostgreSQL을 함께 실행하려면,
매번 긴 명령어를 두 번 입력해야 합니다.
Docker Compose를 쓰면 docker-compose.yml 파일 하나로 관리할 수 있습니다.
docker-compose.yml 예시
version: '3.8'
services:
postgres:
image: postgres:15
container_name: keycloak-postgres
environment:
POSTGRES_DB: keycloak
POSTGRES_USER: keycloak
POSTGRES_PASSWORD: password
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- keycloak-network
keycloak:
image: quay.io/keycloak/keycloak:latest
container_name: keycloak
environment:
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://postgres:5432/keycloak
KC_DB_USERNAME: keycloak
KC_DB_PASSWORD: password
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: admin
command: start-dev
ports:
- "8080:8080"
depends_on:
- postgres
networks:
- keycloak-network
volumes:
postgres_data:
networks:
keycloak-network:실행 명령어
# 시작 (백그라운드)
docker compose up -d
# 로그 확인
docker compose logs -f
# 특정 서비스 로그
docker compose logs -f keycloak
# 중지
docker compose down
# 볼륨까지 삭제
docker compose down -vdocker-compose.yml이 있는 폴더에서 실행하면 됩니다.
최신 Docker에서는 docker-compose 대신 docker compose (하이픈 없음)를 사용합니다.
둘 다 동작하지만, 새 버전 CLI가 더 빠릅니다.
Dockerfile 기초
지금까지는 이미 만들어진 이미지를 가져다 썼습니다.
직접 이미지를 만들려면 Dockerfile이 필요합니다.
프론트엔드 개발자가 Dockerfile을 직접 작성할 일은 많지 않지만,
기본 구조는 알아두면 좋습니다.
기본 구조
# 베이스 이미지
FROM node:20-alpine
# 작업 디렉토리 설정
WORKDIR /app
# 의존성 파일 복사
COPY package.json pnpm-lock.yaml ./
# 의존성 설치
RUN npm install -g pnpm && pnpm install
# 소스 코드 복사
COPY . .
# 빌드
RUN pnpm build
# 포트 노출
EXPOSE 3000
# 실행 명령
CMD ["pnpm", "start"]각 명령어의 의미입니다.
| 명령어 | 설명 |
|---|---|
FROM | 베이스 이미지 지정 |
WORKDIR | 작업 디렉토리 설정 |
COPY | 파일 복사 (호스트 → 이미지) |
RUN | 빌드 시 실행할 명령 |
EXPOSE | 컨테이너가 사용할 포트 명시 |
CMD | 컨테이너 시작 시 실행할 명령 |
이미지 빌드
# 이미지 빌드
docker build -t my-app .
# 태그 지정
docker build -t my-app:1.0.0 ..은 Dockerfile이 있는 현재 디렉토리를 의미합니다.
자주 겪는 문제들
Docker 처음 쓸 때 겪었던 문제들입니다.
포트 충돌
Error response from daemon: driver failed programming external connectivity
on endpoint: Bind for 0.0.0.0:8080 failed: port is already allocated이미 다른 프로세스가 해당 포트를 사용 중입니다.
# 포트 사용 중인 프로세스 확인 (Mac/Linux)
lsof -i :8080
# 또는 다른 포트로 매핑
docker run -d -p 8081:8080 nginx컨테이너가 바로 종료됨
docker run ubuntu
# 바로 종료됨컨테이너는 실행할 프로세스가 없으면 종료됩니다.
대화형으로 실행하려면 -it 옵션이 필요합니다.
docker run -it ubuntu /bin/bash디스크 용량 부족
# 사용하지 않는 리소스 정리
docker system prune
# 볼륨까지 정리 (주의: 데이터 삭제됨)
docker system prune -a --volumesDocker는 이미지와 컨테이너를 계속 쌓아두기 때문에
주기적으로 정리해주는 게 좋습니다.
컨테이너 간 통신 안 됨
같은 네트워크에 있지 않으면 컨테이너끼리 통신이 안 됩니다.
# 네트워크 생성
docker network create my-network
# 컨테이너 실행 시 네트워크 지정
docker run -d --network my-network --name db postgres
docker run -d --network my-network --name app my-appDocker Compose를 쓰면 자동으로 같은 네트워크에 묶여서 편합니다.
유용한 팁
.dockerignore
.gitignore처럼 이미지에 포함하지 않을 파일을 지정합니다.
node_modules
.git
.env
*.log
distnode_modules를 제외하면 이미지 빌드가 훨씬 빨라집니다.
자주 쓰는 명령어 조합
# 모든 컨테이너 중지
docker stop $(docker ps -q)
# 모든 컨테이너 삭제
docker rm $(docker ps -aq)
# 모든 이미지 삭제
docker rmi $(docker images -q)alias로 등록해두면 편합니다.
alias dstop='docker stop $(docker ps -q)'
alias drm='docker rm $(docker ps -aq)'
alias drmi='docker rmi $(docker images -q)'Docker Desktop
CLI가 익숙하지 않다면 Docker Desktop을 설치하면 됩니다.
GUI로 컨테이너 관리, 로그 확인 등을 할 수 있습니다.
Windows, Mac 모두 지원합니다.
정리
프론트엔드 개발자 관점에서 Docker의 핵심입니다.
- 이미지: 컨테이너를 만들기 위한 템플릿 (설치 파일)
- 컨테이너: 이미지를 실행한 인스턴스 (실행 중인 프로그램)
- 볼륨: 데이터를 영구 저장하기 위한 방법
- Docker Compose: 여러 컨테이너를 한 번에 관리
자주 쓰는 명령어만 기억하면 됩니다.
docker run -d -p 호스트:컨테이너 이미지명 # 실행
docker ps # 목록
docker logs -f 컨테이너명 # 로그
docker stop/start/rm 컨테이너명 # 관리
docker compose up -d / down # Compose처음에는 "이걸 왜 배워야 하지?" 싶었는데,
한 번 익숙해지니까 로컬 개발 환경 구축이 훨씬 편해졌습니다.
Keycloak뿐만 아니라 Redis, PostgreSQL, MongoDB 등도
명령어 한 줄이면 바로 띄울 수 있습니다.
프론트엔드 개발자도 Docker 기초 정도는 알아두면 좋다고 생각합니다.