Programming/Spring Boot

Spring Boot, MySQL, JPA 그리고 docker-compose 예제 ... 2/2

Cloud Applicaiton Architect 2021. 9. 29. 13:29
반응형

 

 

Spring Boot, MySQL, JPA 그리고 docker-compose 예제 ... 1/2 에 이어서 계속 진행

 

build.gradle 파일 수정

SimpleBoard에서는 Lombok, Swagger, JPA등과 관련된 라이브러리를 사용할 예정이다. 비록 Spring Initializr를 통해 필요한 dependency를 자동으로 생성했다고 할지라도 Lombok, Swagger등은 아래와 같이 별도로 build.gradle 파일에 추가가 필요하다.

 

*아래의 파일 내용을 전체 카피해서 붙여 넣기 해도 된다.

plugins {
    id 'org.springframework.boot' version '2.1.9.RELEASE'
    id 'io.spring.dependency-management' version '1.0.8.RELEASE'
    id 'java'

}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
    mavenCentral()
}

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

jar {
    baseName="${group}"
    version="${version}"
    manifest{
        attributes 'Title': 'SimpleBoard',
                'Version': 1.0,
                'Main-Class': 'com.example.board.BoardApplication'
    }
    from {
        configurations.compile.collect {
            it.isDirectory() ? it : zipTree(it)
        }
    }
}

dependencies {

    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    runtimeOnly 'mysql:mysql-connector-java'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'

    compile 'io.springfox:springfox-swagger2:2.6.1'
    compile 'io.springfox:springfox-swagger-ui:2.6.1'
}

 

java package 구성

board 어플리케이션 개발을 위해 패키지(디렉토리) 구조를 아래와 같이 구성하도록 하자.

 

src/main/java/com/example/board 아래에 web, domain, service 패키지를 만들어 아래의 이미지 처럼 되도록 한다.

 

 

도메인 클래스 작성

도메인 클래스는 비즈니스 모델을 담당하는 클래스이다.

먼저 src/main/java/com/example/board/domain/Board.java 라는 엔티티 클래스를 만들도록 한다. 엔티티 클래스는 JPA에서 데이터베이스의 테이블과 1:1 대응을 하는 클래스이다. 이 엔티티 클래스에 데이터가 변경되었을때 실제 테이블의 데이터도 함께 변경된다.

 

Board.java

package com.example.board.domain;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import javax.persistence.*;
import java.time.LocalDateTime;

@Entity
@EntityListeners(AuditingEntityListener.class)
@NoArgsConstructor
@Getter
@Setter
public class Board {
    @Id
    @GeneratedValue (strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length = 10)
    private String author;

    @Column(nullable = false, length = 100)
    private String title;

    @Column (length = 500)
    private String content;

    @CreatedDate
    private LocalDateTime createdAt;

}

 

다음으로 Board 엔티티 클래스의 CRUD를 조작할 Repository interface(클래스가 아니다)를 JpaRepository 를 상속받아 구현하도록 한다. 이 Interface의 이름은 BoardRepository.java라는 이름으로 com/example/board/domain 패키지 내에 아래와 같이 만들도록 한다.

 

BoardRepository.java

package com.example.board.domain;


import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface BoardRepository extends JpaRepository<Board, Long> {
    // findBy + 조회할 키 값 지정
    public Board findByAuthor(String author);

    // order by를 키 값으로 하기 위해 findAll 뒤에 ByOrderBy + 키 + Asc/Desc 지
    public List<Board> findAllByOrderByIdDesc();

    public Board findBoardById(Long id);
}

 

JDBC, JPA를 포함한 환경 설정(application.properties)

우리가 사용하는 git의 console-run 브랜치는 앞서 개발 환경을 위해 사용하겠다고 했었다. docker-compose 환경에서는 SimpleBoard 어플리케이션과 MySQL이 Link를 통해 개발 환경에서는 MySQL만 docoker container내부에서 운용할 예정이기때문에 JDBC connection String에 docker container에 접근하기 위해 우선은 IP를 직접 입력하도록 하자.

 

또한 JPA 환경역시 DDL 문장을 프로그램이 직접 생성하도록 지정 하도록 하겠다.

 

/src/main/resources/ 디렉토리 아래에 application.properties 파일을 아래와 같이 만들도록 한다.

 

application.properties 파일의 내용을 아래와 같이 작성합니다.

application.properties

# API 호출시, SQL 문을 콘솔에 출력합니다.
spring.jpa.show-sql=true


# @Entity annotation 을 분석해 DDL을 생성합니다.
spring.jpa.generate-ddl=true


# MySQL 관련 설정
spring.datasource.url=jdbc:mysql://localhost:3306/sample?createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=UTF-8&characterSetResults=UTF-8&useSSL=true
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver


# JPA 설정
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect


# ansi 컬러 출력 활성화
spring.output.ansi.enabled=always


# 기본 로그 레벨 설
logging.level.com=info

 

Service 클래스 작성

src/main/java/com/example/board/service/BoardService.java 클래스를 생성하도록 한다.

Service 클래스는 Controller로 들어온 Service Request를 Domain으로 넘겨주는 중계자 역할을 수행하며 필요한 경우 트랜잭션 관리도 수행한다.

 

BoardService.java는 아래와 같이 작성하도록 한다.

BoardService.java

package com.example.board.service;

import com.example.board.domain.Board;
import com.example.board.domain.BoardRepository;
import com.example.board.web.BoardRequest;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

@Service
public class BoardService {
    @Autowired
    private BoardRepository boardRepository;

    public Long save(BoardRequest boardRequest) {
        Board board = new Board();
        BeanUtils.copyProperties(boardRequest, board);
        board = boardRepository.save(board);
        return board.getId();
    }

    public List<Board> findAllByOrderByIdDesc() {
        return boardRepository.findAllByOrderByIdDesc();
    }

    public Board findBoardById(Long id) {
        return boardRepository.findBoardById(id);
    }

    public void delete(Long id) {
         boardRepository.deleteById(id);
    }

    public Long update(BoardRequest boardRequest) {
        // TODO
      
        return null;
    }
}

 

 

web package 작성

 

src/main/java/com/example/board/web 아래에 BoardController.java 클래스를 만들도록 합니다.

BoardController.java

package com.example.board.web;

import com.example.board.domain.Board;
import com.example.board.service.BoardService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
public class BoardController {
    @Autowired
    private BoardService boardService;

    @RequestMapping(value = "/insert", method = RequestMethod.POST)
    public ResponseEntity<String> save(@RequestBody BoardRequest boardRequest) {
        try {
            Long ret = boardService.save(boardRequest);
            return new ResponseEntity<>("Success", HttpStatus.OK);
        } catch(Exception e) {
            return new ResponseEntity<>("Error", HttpStatus.OK);
        }

    }

    @RequestMapping(value="/", method = RequestMethod.GET)
    public ResponseEntity<List<Board>> findAll() {
        return new ResponseEntity<>(boardService.findAllByOrderByIdDesc(), HttpStatus.OK);
    }


    @RequestMapping(value="/find/{id}", method = RequestMethod.GET)
    public ResponseEntity<Board> findBoardById(@PathVariable("id") Long id) {
        return new ResponseEntity<>(boardService.findBoardById(id), HttpStatus.OK);
    }

    @RequestMapping(value="/delete/{id}", method = RequestMethod.DELETE)
    public ResponseEntity<String> delete(@PathVariable("id") Long id) {
        try {
            boardService.delete(id);
            return new ResponseEntity<>("Success", HttpStatus.OK);
        } catch(Exception e) {
            return new ResponseEntity<>("Error", HttpStatus.OK);
        }
    }
    @RequestMapping(value = "/update", method = RequestMethod.PUT)
    public ResponseEntity<String> update(@RequestBody BoardRequest boardRequest) {
        boardService.save(boardRequest);
        return new ResponseEntity<>("Success", HttpStatus.OK);
    }

}

 

다음으로 화면에서 사용자가 입력한 데이터를 받아다가 Entity에 전달 해 줄 BoardRequest 객체를 web package 아래 아래와 같이 만들도록 하자.

 

/src/main/java/com/example/board/web/BoardRequest.java

BoardRequest.java

package com.example.board.web;

import lombok.Getter;
import lombok.Setter;

@Setter @Getter
public class BoardRequest {
    private Long id;
    private String author;
    private String title;
    private String content;
}

 

 

Swagger 환경 설정

 

Swagger는 REST Api를 효과적으로 사용 할 수 있게 도와줍니다. SimpleBoard 어플리케이션은 별도의 화면개발없이 Swagger를 통해 화면 테스트를 할 예정이므로 Swagger 관련 설정을 Board 프로젝트에 해야 합니다.

/src/main/java/com/example/board 패키지 아래 SwaggerConfig.java 파일을 만들고 아래와 같이 작성하도록 합니다.

SwaggerConfig.java

package com.example.board;


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;


@Configuration
@EnableSwagger2
public class SwaggerConfig {
    // REST API 검색 대상을 com.example.board.web 아래 패키지로 한정시킨다.
    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
        .select()
        .apis(RequestHandlerSelectors.basePackage("com.example.board.web"))
        .paths(PathSelectors.any())
        .build();
    }
}

 

BoardApplication.java 수정

/src/main/java/com/example/board 아래에 있는 SimpleBoard의 Main 클래스인 BoardApplication.java 파일을 다음과 같이 수정한다.

BoardApplicaiton.java

package com.example.board;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;


@SpringBootApplication
@EnableJpaAuditing //JPA Entity 클래스내 @CreatedDate 어노테이션 활성화를 위한 어노테이션
public class BoardApplication {
    public static void main(String[] args) {
        SpringApplication.run(BoardApplication.class, args);
    }
}

 

 

MySQL Docker image 설치

커맨드 콘솔을 기동해 docker pull mysql 명령어를 실행해 최신버전의 MySQL image를 다운로드 받도록 하자.

 

docker pull mysql

docker image가 정상적으로 설치되었는지 확인해 보도록 한다.

docker images

 

MySQL container 실행

 

앞서 다운로드 받은 MySQL docker image를 기반으로 로컬포트 3306에서 내부포트 3306으로 접근 가능하며 root 계정의 패스워드는 root로 해서 mysql container를 만들도록 하자.

 

도커 실행 명령어는 다음과 같이 실행한다.

 

docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root --name mysql mysql

 

정상적인 도커 컨테이너가 생성되었는지 docker -ps를 통해 확인해 보도록 하자.

컨테이너 생성과 기동 확인

 

SimpleBoard 어플리케이션 기동

 

IntelliJ의 터미널에서 gladlew bootRun 타스크를 실행 시켜 SimpleBoard 어플리케이션이 기동되는 것을 확인한다.

gradlew bootRun

 

정상적인 기동이 되었다면 http://localhost:8080/swagger-ui.html 을 호출해 보도록 한다.

 

현재까지 모든 작업이 정상적이었다면 swagger-ui.html이 정상 기동되는 것을 확인 할 수 있다.

 

board-controller를 클릭해 우리가 만든 모든 REST API를 확인하도록 한다.

 

 

insert를 클릭해 boardRequest를 입력 JSON규격에 맞게 입력 한 후 "Try out"을 클릭해 데이터 입력을 확인한다.

 

Try out

입력 JSON 포맷이 정상이었다면 Response Body로 Success 메시지를 받으면서 200번 코드가 리턴된 것을 확인 할 수 있다.

 

실제 MySQL 데이터베이스에 데이터가 들어갔는지 콘솔을 열어 확인해 보도록 하자.

도커 컨테이너 내부 mysql 접속: docker exec -ti mysql /bin/bash

mysql 로그인: mysql -u root -p

password: root

 

docker exec -ti mysql /bin/bash

use sample 을 통해 sample db로 전환

swagger를 통해 입력한 데이터를 확인

 

Git commit, tag 그리고 push

 

git add .을 통해 현재까지 작업을 staging 시킨다.

git add .

 

git commit을 통해 레파지토리에 커밋한다.

git commit -m "console-run commit"

 

커밋이 완료되면 현재의 커밋에 v2.0.0이란 어노테이션을 추가한다.

git tag -a v2.0.0 -m "version 2.0"

 

로컬 git 레파지토리의 내용을 리모트 git 레파지토리로 push 한다.

git push origin console-commit

 

완료 후 git log를 보면 아래와 같다.

 

 

이번 포스팅은 여기까지가 끝~

 

이 포스팅에서 사용한 프로그램 소스는 아래의 git에서 확인 할 수 있다.

 

 

GitHub - sharplee7/simple-board

Contribute to sharplee7/simple-board development by creating an account on GitHub.

github.com

 

반응형

'Programming > Spring Boot' 카테고리의 다른 글

Spring Boot tomcat access log 출력  (0) 2022.01.06
Spring Boot, MySQL, JPA 그리고 docker-compose 예제 ... 1/2  (0) 2021.09.29
Spring Boot AutoConfiguration  (0) 2021.07.07
Spring Initializr  (0) 2021.07.07
Spring Boot 란?  (0) 2021.07.07