[Docker] Spring Boot + MySQL 구동시키기

임성채·2024년 7월 18일

Docker

목록 보기
2/3
post-thumbnail

👨‍💻 Intro

이전에는 Docker를 사용해서 Spring Boot를 컨테이너에 빌드시켜 실행까지 했었는데요.
이번에는 Spring Boot의 MySQL을 연동시켜 간단하게 게시글 제목만 작성해보는 프로젝트를 진행해보았습니다.

📚 Settings

스프링 스타터

  • Spring Web
  • Spring Data JPA
  • Lombok
  • Thymeleaf
  • JDBC API
  • MySQL Driver

프로젝트 구성

├─java
│  └─com
│      └─example
│          └─docker_test
│              │  DockerTestApplication.java
│              │
│              ├─controller
│              │      HomeController.java
│              │
│              ├─entity
│              │      Contents.java
│              │
│              └─repository
│                      ContentsRepository.java
│
└─resources
    │  application.properties
    │
    ├─static
    └─templates
        └─view
                form.html
                list.html

Dockfile

# JDK 17 기반
FROM openjdk:17

# build할 jar 파일 변수 선언
ARG JAR_FILE=build/libs/*.jar

# build/libs에 있는 JAR파일을 Docker 이미지의 app.jar로 복사
COPY ${JAR_FILE} app.jar

# Docker 컨테이너가 실행되면 java -jar app.jar 명령어를 실행하여 Spring boot 실행
ENTRYPOINT ["java", "-jar", "app.jar"]

💻 Code

Entity - Repositoty - Controller - View 구조로 진행하였습니다.

- entity/Contents

@Entity 란?

@Entity가 붙은 클래스는 JPA가 관리하는 객체

주의

  • 기본 생성자 필수(파라미터가 없는 public 또는 protected 생성자)
  • final 클래스, enum, interface, inner 클래스 사용 X
  • 저장할 필드에 final 사용 X
  • 또한 JPA @Entity 에서는 primary key를 지정하기 위해선 jakarta.persistence.Id 을 사용해야 합니다.
    만약 에러가 난다면 @Id의 라이브러리를 체크해줘야합니다. org.springframework.data.annotation.Id 어노테이션은 Spring Data에서 사용되며, JPA 엔티티에는 적절하지 않습니다.
  • 참고
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class Contents {

    @Id @GeneratedValue
    private Long id;

    private String content;

    public Contents(String content) {
        this.content = content;
    }
}

- repository/ContentsRepository

간단한 CRUD 기능만 사용하기때문에 CrudRepository 사용

CrudRepository

  • CRUD 기능을 제공하는 인터페이스
  • org.springframework.data.repository
  • 인터페이스 Repository 를 확장함
  • 참고
public interface ContentsRepository  extends CrudRepository<Contents, Long> {
}

- controller/HomeController

CrudRepositoryCreate(저장)save(enitiy)Read(조회)finaAll() 사용

@RequiredArgsConstructor
@Controller
public class HomeController {
    private final ContentsRepository contentsRepository;

    @GetMapping("/")
    public String home(){
        return "view/form";
    }

    @PostMapping("/")
    public String form(@ModelAttribute ContentRequest content){
        contentsRepository.save(new Contents(content.getContent()));
        return "redirect:/";

    }

    @GetMapping("/list")
    public String list(Model model){
        List<Contents> contents = new ArrayList<>();
        Iterable<Contents> contentsIterable = contentsRepository.findAll();
        contentsIterable.forEach(contents::add);

        model.addAttribute("contents", contents);
        return "view/list";
    }

    @Data
    @AllArgsConstructor
    static class ContentRequest{
        private String content;
    }

}

- view/form.html

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Title</title>
</head>
<body>
<form action="/" method="post">
    <input type="text" id="content" name="content">
    <button type="submit">등록</button>
</form>
<a href="/list">목록보기</a>
</body>
</html>

- view/list.html

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Title</title>
</head>
<body>
<table border="1">
    <thead>
        <tr>
            <td>ID</td>
            <td>Content</td>
        </tr>
    </thead>
    <tbody>
        <tr th:each="c : ${contents}">
            <td th:text="${c.id}"></td>
            <td th:text="${c.content}"></td>
        </tr>
    </tbody>
</table>
</body>
<a href="/">글쓰기</a>
</html>

⭐ application.properties (중요)

MySQL 연동시 많은 에러가 발목을 잡았습니다.

이유는 MySQL 버전 이였습니다.
MySQL 8.x 버전 이상에서는

  • spring.datasource.urluseSSL=false&allowPublicKeyRetrieval=true 를 추가
  • spring.jpa.properties.hibernate.dialectorg.hibernate.dialect.MySQLDialect
    Dialect는 객체 맵핑을 통해 자동으로 쿼리를 작성해주는 것입니다. 원래는 MySQL(버전)Dialect / Ex) MySQL5Dialect
    이런식으로 추가를 하게 되는데 MySQL 8.x 버전 이상에서는 버전명을 입력하지 않는다.
  • spring.jpa.hibernate.ddl-auto 옵션값

참고

spring.application.name=docker_test

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# 로컬 실행
#spring.datasource.url=jdbc:mysql://localhost:3306/mydb?useSSL=false
#spring.datasource.username=root
#spring.datasource.password=0608

# 도커 실행
spring.datasource.url=jdbc:mysql://db-mysql:3306/mydb?useSSL=false&allowPublicKeyRetrieval=true
spring.datasource.username=user
spring.datasource.password=user

spring.jpa.hibernate.ddl-auto=create
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect

⭐ Docker (중요)

패키징(gradle)

./gradlew clean build -x test

build 할때 test에서 DB연결을 해보기때문에 test를 제외하고 build 해줘야함.

-x test : 테스트 작업을 제외하고 빌드를 수행

Docker 네트워크 생성

여러 컨테이너를 동일한 환경에서 연동시키기 위해 네트워크 설정이 필요합니다.

  • 네트워크 생성 명령어
    $ docker network create springboot-mysql-net

  • 생성된 네트워크 확인
    $ docker network ls

Docker MySQL 구동

MySQL이미지 가져오기
$ docker pull mysql

MySQL 구동

  • --name : 컨테이너명 설정
  • -p : 포트매핑
  • --network : 네트워크 설정
  • -e : 환경변수 설정
  • -d : 백그라운드로 컨테이너 실행
$ docker run --name db-mysql -p 3306:3306 --network springboot-mysql-net -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=mydb -e MYSQL_USER=user -e MYSQL_PASSWORD=user -d mysql
  • 구동 시 Docker desktop Containers 화면

Docker Spring Boot 구동

Dockerfile을 이용한 이미지 빌드
$ docker build -t springboot-mysql:1.0

Docker Spring Boot 구동

$ docker run -p 8080:8080 --name springboot-mysql --network springboot-mysql-net -d springboot-mysql:1.0
  • 구동시 Docker desktop Containers 화면

💡 결과


다음에는 docker-compose를 이용해 보겠습니다.

참고

https://velog.io/@dhk22/Springboot-MySQL-Docker-%EB%8F%84%EC%BB%A4%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%84%B1

https://velog.io/@dhkim1522/Spring-JPA-%EC%98%AC%EB%B0%94%EB%A5%B8-Entity-%EC%82%AC%EC%9A%A9%EB%B2%95

https://bibi6666667.tistory.com/283

https://abruption.github.io/posts/Java-mysql-error/

https://velog.io/@mercurios0603/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-JPA-MySQL-8.0-%EC%84%A4%EC%A0%95

https://2dongdong.tistory.com/66

0개의 댓글