Project는 Gradle로 선택을 하고 언어는 자바를 선택한다.
ADD DEPENDENCIES부분에
이렇게 설정해 주었다.
요약 정리
데이터 베이스는 mysql을 사용할것이며 중간 하이버네이트로 JPA를 두고 CRUD를 구현할것이다.
템플릿 엔진은 타임리프를 사용하며, Lombok이라는 getter, setter 등 다양한 편의를 제공하는 라이브러리를 추가한다.
실제 저 이름들이 보이는 걸 ADD DEPENDENCIES에 추가하면 사진과 같이 저절로 의존관계를 맺어서 프로젝트가 생성된다.
프로젝트를 생성하게되면 위와같이 build.gradle에 생기게 되고 나는 war로 빌드해서 실행시키고 싶어서 packaging을 war로 선택하였다.
sourceCompatibility = '11'
targetCompatibility = '11'
spring.application.name=***
server.port=8080
server.servlet.context-path=/
# MySQL 설정
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# Public
# DB Source URL
#spring.datasource.url=jdbc:mysql://***:3306/***
# DB username
#spring.datasource.username=***
# Internal
# DB Source URL
spring.datasource.url=jdbc:mysql://***/daeduk
# DB username
spring.datasource.username=***
# DB password
spring.datasource.password=***
# true 설정시 JPA 쿼리문 확인 가능
spring.jpa.show-sql=true
# DDL(create, alter, drop) DB의 고유 기능
#spring.jpa.hibernate.ddl-auto=update
#spring.jpa.hibernate.ddl-auto=validate
#spring.jpa.hibernate.ddl-auto=create
#spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.hibernate.ddl-auto=none
# JPA의 구현체인 Hibernate가 동작하면서 발생한 SQL의 가독성
spring.jpa.properties.hibernate.format_sql=true
# 타임 리프 템플릿 설정
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.enabled=true
# 개발 중에는 캐시를 비활성화
spring.thymeleaf.cache=false
# Spring MVC 뷰 리졸버 설정
spring.mvc.view.prefix=/WEB-INF/classes/templates/
spring.mvc.view.suffix=.html
#하이버네이트에게 모든 데이터베이스 객체 이름(테이블, 컬럼 등)을 쿼팅!
spring.jpa.properties.hibernate.globally_quoted_identifiers=true
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
주석된 부분으로 각각의 설정이 어떤걸 도와주는지 유추해볼수있다.
gitignore - Git에서 특정 파일이나 디렉토리를 버전 관리에서 제외할 때 사용된다. 주로 커밋되어지면 안되는거나, 충돌날 부분들이 안에 적혀있다.
build.gradle- 프로젝트의 빌드 설정, 의존성, 플러그인 등을 정의하는 파일로써 gradle이 빌드할때 해당 설정파일을 참조한다.
gradlew - Unix 기반 시스템에서 Gradle Wrapper를 실행하는 스크립트이다, 즉 gradlew build를 통해 빌드하게 되는것!!
gradlew.bat - Windows 시스템에서 Gradle Wrapper를 실행하는 스크립트이다.
settings.gradle - 프로젝트의 설정을 정의하는 파일로, 주로 멀티프로젝트 빌드에서 사용된다.
이제 프로젝트 구성을 알아보자... 참고로, 초급 개발자여서 프로젝트구성이 많이 어설프거나 부족한 부분은 참고하며 다양한 블로그 및 강의를 통하여 더욱더 좋은 설계를 하면 된다.
@Controller
public class TestController {
@Autowired
private final TestService testService;
@Autowired
public TestController(TestService testService) {
this.testService = testService;
}
@GetMapping("/test")
public String index(Model model) {
TestDto testDto = testService.getTestValue();
model.addAttribute("message", testDto.getCustName());
return "test";
}
}
클라이언트가 요청한걸 웹컨테이너안의 Servlet이 해당 url을 찾아 서비스의 결과를 응답한다.
즉, http://localhost:8080/test를 요청하면 해당 메서드를 servlet이 찾아서 결과를 클라이언트에게 응답하는 구조이다.
중간의 Interface를 두어서 타이트 커플링을 막는다.
public interface TestService {
public TestDto getTestValue();
}
@Service
public class TestServiceImple implements TestService {
@Autowired
TestRepository testRepository;
@Autowired
private ModelMapper modelMapper;
public TestDto getTestValue() {
Optional<TestEntity> testValue = testRepository.findById(1);
TestEntity entity = testValue.orElseThrow(() -> new NotFoundException("not found any data"));
/* entity to dto setting */
modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
TestDto testDto = modelMapper.map(entity, TestDto.class);
return testDto;
}
}
데이터베이스에서 하이버네이트가 CRUD를 잘 구현하는지를 테스트해보기 위해 간단한 서비스를 만들고 JPA 인터페이스를 사용해서 구현한다.
JPA 내장 함수를 사용하게 되면 Optional이라는 리턴타입으로 받아지게 되며 해당 타입은 안에 다양한 내장함수를 포함하고있어 orElseThrow()처럼 만약 조회해온 결과값이 없거나 그럴때 바로 내가 지정한 에러로 리턴할수 있는 장점이 있다.
그리고 modelMapper라는 객체가 Entity를 DTO로 매핑해주는 라이브러리가 있기때문에 Entity객체를 DTO로 매핑할때 사용하면 아주 편리하다.
public interface TestRepository extends JpaRepository<TestEntity,Integer>{
}
JPA인터페이스를 상속받아서 해당 인터페이스 안에 있는 다양한 CRUD내장함수를 이용하거나 Naming strategy를 사용하여 다양한 CRUD를 구현할수 있다.
public class NotFoundException extends RuntimeException {
public NotFoundException(String message) {
super(message);
}
}
@Entity
@Data
@Table(name = "CUSTOMER")
public class TestEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Integer id;
@Column(name = "cust_name", nullable = false)
private String custName;
}
JPA는 더이상 DBA의 권한에서 조금더 자유로운 개발자가 DDL을 사용할수 있게 해준다.
@Entity를 사용해서 해당 테이블명으로 테이블을 만들수가 있다.
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TestDto {
private Integer id;
private String custName;
}
DTO는 data tranfer object로써 데이터를 담아서 레이어마다 값을 보내주고 받는 역할을 한다.
Entity객체에 담아서 보내는것보다 Dto라는 객체에 담아서 보내는것이 오염의 방지도 있고 이런식으로 MVC설계를 한다.
@Configuration
public class ModelMapperConfig {
@Bean
public ModelMapper modelMapper() {
return new ModelMapper();
}
}
이렇게 프로젝트를 구성하고, 해당 프로젝트는 gradle로 빌드한다.
나는 해당 프로젝트를 클라우드 서버에 배포해야해서 Git의 webhook이라는 서비스를 사용해서
개발을 하고 깃 래파지토리에 커밋을 하게 되면 POST요청으로 내 클라우드 서버에 요청하게된다.
클라우드 서버에서 webhook이라는 데몬이 post요청을 받으면 내가 가지고 있는 쉘 스크립트를 구동해서 해당 프로젝트를 clone해와서 빌드해서 war파일을 실행하는 방식으로 CICD를 간단하게 구현했다.