프로젝트 다운로드 후 IntelliJ로 Import or Open
프로젝트를 실행해보면 tomcat이 8080 port에서 실행이 되었다는 로그가 뜸
IntelliJ > Preference > Plugins > Lombok
IntelliJ > Preference > Build, Execution, Deployment > Compiler > Annotation Processors
Enable annotation processing 선택
Project에서 아래와 같이 실행하면 의존관계를 보여줌
$ ./gradlew dependencies
IntelliJ의 우측 Gradle 탭에서도 dependency 확인 가능
spring-boot-starter-web
아래엔 embedded된 tomcat
과 webmvc
가 있음.Tomcat은 Java를 이용한 웹 애플리케이션의 다양한 규격(Spec)을 준수하여 JSP, HTML 파일들로 구성된 .war 파일(애플리케이션의 파일들을 압축해놓은 하나의 파일)을 배포해주는 엔진
spring-boot-starter-data-jpa
아래엔 core
, aop
, jdbc
, HikariCP
(커넥션 풀), hibernate
, logging
(logback
, slf4j
- logger를 찍는 인터페이스들의 모음) 등이 있음.spring-boot-starter-test
에는 junit
과 mockito
, assertj
등이 있음.
thymeleaf 공식 사이트: https://www.thymeleaf.org/
스프링 공식 튜토리얼: https://spring.io/guides/gs/serving-web-content/
스프링 부트 thymeleaf가 viewName 매핑
resources:templates/
+ {ViewName} + .html
@Controller
public class HelloController {
@GetMapping("hello")
public String hello(Model model) {
model.addAttribute("data", "hello!!");
return "hello";
}
}
Controller에서 Model
에 data를 싣어 View에 넘김 → return "ViewName"
(.html이 생략됨)
thymeleaf 템플릿엔진 동작 확인(hello.html) - 서버사이드 렌더링
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Hello</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<p th:text="'안녕하세요. ' + ${data}" >안녕하세요. 손님</p>
</body>
</html>
순수 html 렌더링 시 "안녕하세요. 손님"이 출력, 서버 사이드 렌더링 시 안녕하세요. data
가 출력.
static/index.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Hello</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
Hello
<a href="/hello">hello</a>
</body>
</html>
참고) spring-boot-devtools
라이브러리를 추가하면, html 파일을 컴파일만 해주면 서버 재시작 없이 View 파일 변경이 가능하다.
implementation 'org.springframework.boot:spring-boot-devtools'
html 파일 컴파일 방법: 메뉴 build → Recompile
개발이나 테스트 용도로 가볍고 편리한 DB, 웹 화면 제공
jdbc:h2:~/jpashop
(최소 한번, 세션키 유지한 상태로 실행)~/jpashop.mv.db
파일 생성 확인jdbc:h2:tcp://localhost/~/jpashop
이렇게 접속
**main/resources/application.yml**
spring:
datasource:
url: jdbc:h2:tcp://localhost/~/jpashop;MVCC=TRUE
username: sa
password:
driver-class-name: org.h2.Driver
jpa:
hibernate:
ddl-auto: create
properties:
hibernate:
# show_sql: true
format_sql: true
logging:
level:
org.hibernate.SQL: debug
ddl-auto: create
옵션은 애플리케이션 실행 시점에 테이블을 drop 하고, 다시 생성한다.show_sql
옵션은 System.out 에 하이버네이트 실행 SQL을 남긴다.org.hibernate.SQL
옵션은 logger를 통해 하이버네이트 실행 SQL을 남긴다.주의) application.yml 같은 yml 파일은 띄어쓰기(스페이스) 2칸으로 계층을 만든다.
회원 엔티티
@Entity
@Getter @Setter
public class Member {
@Id @GeneratedValue
private Long id;
private String username;
}
회원 리포지토리
@Repository
public class MemberRepository {
@PersistenceContext
private EntityManager entityManager;
public Long save(Member member) {
entityManager.persist(member);
return member.getId();
}
public Member findById(Long id) {
return entityManager.find(Member.class, id);
}
}
@PersistenceContext 어노테이션이 있으면, 스프링 부트가 엔티티 매니저를 생성해 주입 시켜준다.
save()
에서 Member가 아닌 Member.getId()
를 반환하는 이유: 커맨드와 쿼리를 분리하라테스트
MemberRepository의 test니 @Autowired로 memberRepository Injection을 받는다.
@RunWith(SpringRunner.class)
@SpringBootTest
public class MemberRepositoryTest {
@Autowired
MemberRepository memberRepository;
@Test
public void save() {
//given
Member member = new Member();
member.setUsername("member1");
//when
Long savedId = memberRepository.save(member);
Member findMember = memberRepository.findById(savedId);
//then
Assertions.assertThat(savedId).isEqualTo(findMember.getId());
Assertions.assertThat(member.getUsername()).isEqualTo(findMember.getUsername());
Assertions.assertThat(findMember).isEqualTo(member);
}
}
테스트 실행 시 에러: No EntityManager with actual transaction available for current thread
→ EntityManager를 통한 모든 데이터 변경은 항상 transaction 안에서 이루어져야 한다.
→ (Repository 또는 메서드에 @Transactional을 걸 수 있지만) 일단 Test에 추가해준다.
테스트 성공: H2 데이터베이스에 Member 테이블이 생성된 것을 확인할 수 있다.
@Transactional 어노테이션이 테스트에 있으면 테스트 후 Rollback을 하므로, 저장된 데이터는 없다.
@Rollback(false) 어노테이션 추가 시 Rollback없이 커밋해 테스트 내용이 DB에 저장된다.
Assertions.assertThat(findMember).isEqualTo(member);
결과는 두 객체가 동일
→ 같은 트랜잭션 안에서 데이터를 조회하고 저장하면 영속성 컨텍스트가 동일하다. 같은 영속성 컨텍스트 안에서는 id
(식별자)값이 같으면 같은 엔티티로 식별한다. 1차 캐시에 저장된다.
org.hibernate.type: trace
쿼리 상에는 "?"로 남지만 SQL 실행 파라미터를 로그로 남긴다.
implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.5.6'
참고) 쿼리 파라미터를 로그로 남기는 외부 라이브러리는 시스템 자원을 사용하므로, 개발 단계에서는 편하 게 사용해도 된다. 하지만 운영시스템에 적용하려면 꼭 성능테스트를 하고 사용하는 것이 좋다.