목적(간단한 투두리스트 앱 만들기)
간단한 투두 리스트 서비스를 만들고 이를 컨테이너화 해서 구현하려 합니다.
프론트, 백엔드, 디비를 모두 컨테이너로 만들고 같은 네트워크에서 동작시키는 테스트 과정입니다.
프론트

프론트 기능은 하나의 페이지에서 투두 추가, 투두 리스트 보여주기 두 가지입니다.
간단한 리액트 앱을 노드 기반의 이미지로 만들고 미리 만들어둔 브릿지 네트워크로 넣어 실행하겠습니다.
dockerfile
FROM node:14
WORKDIR /app
COPY package.json .
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
docker run -d -p 3000:3000 --network todo-network --name todo-front-container todo-front
백엔드
백엔드의 기능은 두 가지 입니다. 먼저 get 방식으로 /todos 를 호출하면 모든 투두를 줍니다. 그리고 post 방식으로 투두를 주면 이를 db에 저장합니다.
dockerfile
FROM gradle:jdk17 AS build
WORKDIR /app
COPY . /app
RUN gradle clean build --no-daemon
FROM openjdk:17
WORKDIR /app
COPY --from=build /app/build/libs/*.jar /app/todo.jar
EXPOSE 8080
ENTRYPOINT ["java"]
CMD ["-jar", "todo.jar"]
먼저 gradle이미지로 todo.jar을 만들고 이를 jdk 환경에서 복사해서 이미지로 만들었습니다(멀티 스테이징).
곧바로 gradle로 이미지를 만들 때보다 용량이 훨씬 적습니다.
문제발생
<aside> 🛠 개발환경 java 17
springboot 3.2.5
gradle 8.7
</aside>
위의 환경에서 docker image가 생성이 안되는 문제가 발생합니다.
#0 44.36 TodoListBackendApplicationTests > contextLoads() FAILED
#0 44.36 java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:180
#0 44.36 Caused by: org.springframework.beans.factory.BeanCreationException at AbstractAutowireCapableBeanFactory.java:1786
#0 44.36 Caused by: org.hibernate.service.spi.ServiceException at AbstractServiceRegistryImpl.java:276
#0 44.36 Caused by: org.hibernate.HibernateException at DialectFactoryImpl.java:191
#0 44.43
#0 44.43 1 test completed, 1 failed
처음에는 gradle 버전이 문젠가 해서 바꿔줘봤지만 여전히 실패했습니다.
검색을 해보니 main에는 application.properties 파일이 존재하지만 test에는 application.properties 파일이 없어서 발생하는 문제라고 합니다.
이번 실습에서는 테스트 코드를 사용하지 않았기 때문에 @SpringBootTest 주석처리해서 해결했습니다.
docker run -d -p 8080:8080 --network todo-network \\
-e MYSQL_HOST=todo-db-container \\
-e MYSQL_USERNAME=root \\
-e MYSQL_PASSWORD=1234567890 \\
--name todo-back-container \\
todo-back
디비와 연동하기 위해 미리 설정해둔 환경변수 값을 넣어줬습니다.
DB
디비는 mysql을 사용하였습니다.
dockerfile
init.sql을 copy하여 스키마와 entity를 생성하도록 생성하도록 설정하였습니다.
FROM mysql
COPY init.sql /docker-entrypoint-initdb.d/
docker run -d -p 3306:3306 \\
--name todo-db-container \\
--network todo-network \\
-e MYSQL_ROOT_PASSWORD=1234567890 \\
todo-db
도커 컴포즈

잘 동작하게 하기 위해서 순서가 중요한데 db가 먼저 실행되고 서버가 나중에 실행되어야 합니다.
이번에는 개별적으로 실행해주어 문제가 없었습니다. 하지만 매번 하나씩 컨테이너를 실행하기에는 너무 번거롭습니다. 그래서 마지막으로 도커 컴포즈를 사용해서 한 번에 하는 방법을 시도해보겠습니다.
version: "3.8"
services:
mysql:
image: mysql:latest
container_name: todo-db-container
environment:
MYSQL_ROOT_PASSWORD: 1234567890
MYSQL_DATABASE: TodoList
volumes:
- ./mysql-init:/docker-entrypoint-initdb.d
- ./todoDB/init.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
test: ["CMD-SHELL", "mysqladmin ping -h localhost -u root -p1234567890"]
interval: 10s
timeout: 5s
retries: 3
start_period: 30s
command: ["--default-authentication-plugin=mysql_native_password"]
spring-boot-app:
build:
context: ./todoList_backend
dockerfile: dockerfile
container_name: todo-back-container
ports:
- "8080:8080"
depends_on:
mysql:
condition: service_healthy
restart: true
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://todo-db-container:3306/TodoList
SPRING_DATASOURCE_USERNAME: root
SPRING_DATASOURCE_PASSWORD: 1234567890
react-app:
build:
context: ./todo-list
dockerfile: dockerfile
container_name: todo-front-container
ports:
- "3000:3000"
원래는 depend on에 mysql만 넣어줬는데, 도커 컴포즈로 실행해보니 backend서비스가 db가 설정되기 전에 실행이 돼서 문제가 발생합니다. healthcheck을 추가해서 정상적으로 작동하도록 수정하였습니다.






