Docker: Dockerizing 실습

KHS·2025년 2월 22일

이 글은 LG CNS AM Camp 1기에서 수강한 내용을 정리한 글입니다.

리액트 앱 dockerizing


REST API 엔드포인트를 환경변수에서 읽어오도록 수정

  • 리액트 앱의 엔드포인트 수정

    const rest_api_host = import.meta.env.VITE_REST_API_HOST;
    const rest_api_port = import.meta.env.VITE_REST_API_PORT;
    console.log({rest_api_host, rest_api_port});
    
    ...
    
    axios
    .get(`http://${rest_api_host}:${rest_api_port}/api/v2/board`, ... )
    
  • 개발 PC에서 동작 확인

    • 리액트 앱에서 사용할 환경변수를 설정
      c:\lgcns\board-app> set VITE_REST_API_HOST=127.0.0.1
      c:\lgcns\board-app> set VITE_REST_API_PORT=8080
    • 개발 서버 실행 (환경변수를 설정한 동일한 터미널에서 실행)
      c:\lgcns\board-app> npm run dev

소스 코드 빌드 ⇒ 배포 버전

  • 빌드

    c:\lgcns\board-app> npm run build
  • Nginx 컨테이너를 이용해서 빌드 결과가 서비스되는 것을 확인

    c:\lgcns\board-app> docker container run -d -p 8888:80 --name web --rm -v c:\lgcns\board-app\dist:/usr/share/nginx/html nginx
    • c:\lgcns\board-app\dist: 빌드 결과
    • /usr/share/nginx/html: nginx 웹 루트 디렉토리
  • http://localhost:8888 접속하면 정상적으로 동작하는 것을 확인

Dockerfile 작성

  • 파일 작성

    FROM	 node:22.13.1 AS builder
    ENV 	 VITE_REST_API_HOST=192.168.0.221   # 스프링부트가 실행되고 있는 서버의 주소 (내 PC 주소)
    ENV 	 VITE_REST_API_PORT=8080
    WORKDIR /app
    COPY	 . .
    RUN 	 npm install
    RUN 	 npm run build
    
    FROM	 nginx
    COPY	 --from=builder /app/dist /usr/share/nginx/html
  • 이미지 빌드

    c:\lgcns\board-app> docker image build -t board-app:v1 .
    c:\lgcns\board-app> docker image ls
    c:\lgcns\board-app> docker container run -d -p 80 --name my-react-app --rm board-app:v1
    c:\lgcns\board-app> docker container ls
  • 컨테이너로 접속했을 때 CORS 오류가 발생 ⇒ 스프링부트의 WebMvcConfiguration 파일에 Origin을 추가

     @Override
     public void addCorsMappings(CorsRegistry registry) {
         registry
             .addMapping("/api/**")
             .allowedOrigins("http://localhost:5173", "http://localhost:59142", "http://localhost:8888")
             .allowedMethods("GET", "POST", "PUT", "DELETE");
         registry
             .addMapping("/loginProc")
             .allowedOrigins("http://localhost:5173", "http://localhost:59142", "http://localhost:8888")
             .allowedMethods("POST");
     }
    
  • 게시판 목록 데이터 요청 주소가 Dockerfile에 ENV 지시어로 정의한 값으로 설정된 것을 확인

빌드 인자값(build argument)을 사용하도록 Dockerfile을 수정

  • 파일 수정

    FROM	node:22.13.1 AS builder
    ARG 	SPRINGBOOT_ADDRESS=192.168.0.221
    ARG 	SPRINGBOOT_PORT=8080
    ENV 	VITE_REST_API_HOST=${SPRINGBOOT_ADDRESS}
    ENV 	VITE_REST_API_PORT=${SPRINGBOOT_PORT}
    WORKDIR /app
    COPY	. .
    RUN 	npm install
    RUN 	npm run build
    
    FROM	nginx
    COPY	--from=builder /app/dist /usr/share/nginx/html
  • 이미지 빌드 및 컨테이너 실행

    c:\lgcns\board-app> docker image build --build-arg SPRINGBOOT_ADDRESS=localhost -t board-app:v2 .
    c:\lgcns\board-app> docker container ls
    
    이전 컨테이너와 같은 포트를 사용하기 위해서 컨테이너를 중지, 삭제 (스프링부트 서버의 CORS 설정을 동일하게 가져가기 위해서)
    c:\lgcns\board-app> docker container stop my-react-app
    
    c:\lgcns\board-app> docker container run -d -p 59142:80 --name my-react-app --rm board-app:v2
    c:\lgcns\board-app> docker container ls
  • 게시판 목록 정보를 조회하는 주소가 --build-arg 옵션에 설정한 값을 사용하는 것을 확인

이미지를 도커 허브에 등록

c:\lgcns\board-app> docker image tag board-app:v2 myanjini/board-app:v2
c:\lgcns\board-app> docker image ls
c:\lgcns\board-app> docker image push myanjini/board-app:v2

MySQL 컨테이너 실행

  • 컨테이너 실행
    c:\lgcns\board-app> docker container run -d -p 4406:3306 --name mysql -e MYSQL_ROOT_PASSWORD=p@ssw0rd -e MYSQL_DATABASE=springbootdb -e MYSQL_USER=springboot -e MYSQL_PASSWORD=p@ssw0rd -v c:\temp\mysql_data:/var/lib/mysql mysql
    c:\lgcns\board-app> docker container ls
  • 새 MySQL 연결 추가

SpringBoot 소스 코드를 수정

  • application-prod.properties

    spring.datasource.url=jdbc:mysql://mysql:3306/springbootdb?useUnicode=true&characterEncoding=utf-8&serverTimeZone=Asia/Seoul
    • mysql:3306의 'mysql': mysql 컨테이너와 연결되는 별칭
    • 컨테이너 내부 통신을 이용하지 않고, 호스트를 거쳐서 통신을 하는 경우 ⇒ HOSTIP:4406
  • application.properties

    • 활성 프로파일이 prod인 것을 확인
    • 업로드 파일의 저장 경로를 수정
    spring.profiles.active=prod
     spring.servlet.multipart.location=/uploads
  • WebMvcConfiguration

    @Override
    public void addCorsMappings(CorsRegistry registry) {
      	registry
          	.addMapping("/api/**")
          	.allowedOrigins("http://localhost:5173", "http://localhost:90")
          	.allowedMethods("GET", "POST", "PUT", "DELETE");
      	registry
          	.addMapping("/loginProc")
          	.allowedOrigins("http://localhost:5173", "http://localhost:90")
          	.allowedMethods("POST");
     }
  • 프로젝트 루트 디렉토리로 이동

    c:\lgcns\board> dir
    c:\lgcns\board> gradlew clean build bootBuildImage -x test
    c:\lgcns\board> docker image ls
    c:\lgcns\board> docker container run -d -p 9090:8080 --name springboot --link mysql:mysql board:0.0.1-SNAPSHOT
    c:\lgcns\board> docker container ls
    • --link mysql:mysql: <컨테이너명>:<별칭>

Dockerfile 수정

  • 파일 수정

    FROM	node:22.13.1 AS builder
    ARG 	SPRINGBOOT_ADDRESS=192.168.0.221
    ARG 	SPRINGBOOT_PORT=9090
    ENV 	VITE_REST_API_HOST=${SPRINGBOOT_ADDRESS}
    ENV 	VITE_REST_API_PORT=${SPRINGBOOT_PORT}
    WORKDIR /app
    COPY	. .
    RUN 	npm install
    RUN 	npm run build
    
    FROM	nginx
    COPY	--from=builder /app/dist /usr/share/nginx/html
  • 이미지 빌드 및 컨테이너 실행

    c:\lgcns\board-app> docker image build --build-arg SPRINGBOOT_ADDRESS=localhost -t board-app:v3 .
    c:\lgcns\board-app> docker image ls
    c:\lgcns\board-app> docker container run -d -p 90:80 --name react board-app:v3
  • 워크벤치를 이용해서 사용자 등록

    insert into `springbootdb`.`t_jpa_user` 
    (`email`, `name`, `password`, `role`, `username`) 
    values 
    ('hong@test.com', '홍길동', '$2a$10$3uNr2G/ZPqPKAbwzZnHb7.AVrdYa6JhwPvrklp3rtwZsKO5ks20Fe', 'ROLE_USER', 'hong')
  • 브라우저를 이용해서 리액트 컨테이너로 접근

docker-compose를 이용해서 멀티 컨테이너 실행

  • docker-compose.yaml 파일을 생성

    services:
      mysql:
        image: mysql
        ports:
        - 4406:3306
        environment:
        - MYSQL_ROOT_PASSWORD=p@ssw0rd
        - MYSQL_DATABASE=springbootdb
        - MYSQL_USER=springboot
        - MYSQL_PASSWORD=p@ssw0rd
        volumes:
        - c:\temp\mysql_data:/var/lib/mysql
    
      springboot:
        depends_on:
        - mysql
        image: board:0.0.1-SNAPSHOT
        ports:
        - 9090:8080
    
      react:
        depends_on:
        - springboot
        image: board-app:v3
        ports:
        - 90:80
  • 기존 컨테이너 모두 삭제 후 docker compose up

    c:\lgcns\board-app> docker compose up -d
    c:\lgcns\board-app> docker container ls
  • 동작 테스트

    • 동일한 호스트 볼륨을 사용하고 있기 때문에 사용자 정보가 그래로 남아 있으므로, 별도의 사용자 추가 없이 테스트 가능

참고 도식

  • 단일 호스트에 여러 컨테이너가 실행되는 경우

  • 여러 호스트에 컨테이너가 실행되는 경우

0개의 댓글