책에서는 URL 설계가 Restful 방식이 아니다.
추후 React + Springboot 프로젝트를 위해서는 Restful API 설계에 대해서도 알아야 할 필요가 있다.
REST의 기본 원칙을 성실히 지킨 서비스 디자인을 RESTful이라고 한다.
과거에는 웹 브라우저만 지원하면 되었지만, 지금의 서버는 웹, 앱 등 다양한 통신을 할 수 있어야 한다. 그래서 범용적인 사용성을 보장하는 서버 디자인이 필요해졌고 여기에 사용되는 것이 Restful API 설계이다.
그래서 Ajax 사용
클라이언트로 웹과 앱이 있을 때, 웹은 서버로부터 html을 응답 받고 앱은 json 데이터를 응답 받는다. 서버를 따로 만들 필요 없이 json 데이터를 응답해주면 된다.(@RestController)
@RestController은 Spring MVC Controlle에 @ResponseBody가 추가된 것이다.
@ResponseBody란, 메소드에 이 어노테이션이 있다면 메소드에서 리턴되는 값은 View 를 통해서 응답하지 않고 HTTP Response Body에 쓰여지게 된다. 이때 리턴되는 데이터 타입에 따라 MessageConverter에서 알맞은 형태로 변환이 이뤄진다.
이외에 Rest API 설계방식 등 자세한 내용에 대해서는 아래 블로그 참고
RESTful API 설계 가이드
REST API 제대로 알고 사용하기
대부분 책의 예제를 보면 Service 계층은 항상 인터페이스 1개에 구현체도 1개인 형태를 보인다. 그럼 굳이 인터페이스를 만드는 이유가 무엇일까?
비즈니스 로직을 담당한다. Controller로 부터 데이터를 받아서 가공하고 요청을 처리한다.
객체지향 언어인 Java에서 인터페이스를 사용하는 목적은
크게 다형성, 유지보수 및 변경 용이, 느슨한 결합 등이 있다.
다형성(polymorphism)이란?
하나의 객체가 여러 가지 타입을 가질 수 있는 것을 의미하는데, Service 계층을 인터페이스로 구현함으로써 다형성을 이용한다고 볼 수 있다.
즉, 비즈니스 로직의 기능을 추가하거나 변경해야하는 상황이 오면 얼마든지 새로운 구현체를 만들어서 쉽게 변경하여 사용할 수 있다. 참고
사용자 정의 쿼리란?
JPA가 자동으로 생성하는 쿼리를 사용하는게 아닌 사용자가 정의한 대로 쿼리가 생성 혹은 데이터베이스에 종속적인 Native Query 가 생성 되는 것
JPA에서 사용자 정의 쿼리를 사용하는 방법
1. Named Query
2. 쿼리 메서드 - 메서드의 이름만으로 필요한 쿼리 생성
3. @Query - 단순한 몇 가지의 검색조건을 만들 때 사용하며, 실행할 메서드 위에 정적 쿼리를 작성, 이때 JPQL 사용
배워보자 Spring Data JPA
복잡한 조합을 이용하는 경우의 수가 많은 상황에서는 동적으로 쿼리를 생성해야 한다. 이때 사용하는 것이 QueryDSL이다.
즉, SQL, JPQL을 자바 코드로 작성할 수 있도록 해주는 빌더 API 개념이며, SQL을 문자열이 아닌 자바 코드로 작성하기 때문에 Type-safety하다. 참고
implementation 'com.querydsl:querydsl-jpa'
def querydslDir = "$buildDir/generated/querydsl"
querydsl {
jpa = true
querydslSourcesDir = querydslDir
}
sourceSets {
main.java.srcDir querydslDir
}
configurations {
querydsl.extendsFrom compileClasspath
}
compileQuerydsl {
options.annotationProcessorPath =
configurations.querydsl
}
compileQuerydsl 실행
Q도메인 클래스 생성 확인
Repository 클래스에 QuerydslPredicateExecuter 인터페이스 상속 추가
사용 예제
// 다중 항목 검색
// 조건1. 제목 혹은 내용에 특정 키워드가 있다.
// 조건2. gno >= 0
@Test
public void testQuery2() {
Pageable pageable =
PageRequest.of(0, 10, Sort.by("gno").descending());
// Q도메인 객체 생성
QGuestbook qGuestbook = QGuestbook.guestbook;
String keyword = "1";
// 조건을 담는 컨테이너
BooleanBuilder builder = new BooleanBuilder();
// 조건 생성
BooleanExpression exTitle = qGuestbook.title.contains(keyword);
BooleanExpression exContent = qGuestbook.content.contains(keyword);
BooleanExpression exAll = exTitle.or(exContent);
// 컨테이너에 조건 결합
builder.and(exAll);
builder.and(qGuestbook.gno.gt(0L));
//QuerydslPredicateExecutor의 findAll 함수에
//조건과 페이지 정보 파라미터로 전달
Page<Guestbook> result =
guestbookRepository.findAll(builder, pageable);
result.stream().forEach(guestbook -> {
System.out.println(guestbook);
});
}
p157 의존성 주입과 final 키워드 사용을 보고 뭐였지?하는 나를 위해...
생성자 주입에서 final를 사용하는 이유 참고
스프링의 핵심 기술 중 하나인 DI(Dependency Injection, 의존성 주입) 방법에는 여러가지가 있다.
1. 필드 주입
2. 수정자 주입
3. 생성자 주입
4. 일반메소드 주입
생성자 주입을 사용해야 하는 이유
1. OCP 원칙을 지키며 객체의 불변성을 확보
2. 테스트 코드의 작성이 용이
3. final 키워드를 사용할 수 있고, Lombok과의 결합을 통한 간결한 코드
4. 순환 참조 문제를 를 애플리케이션 구동(객체의 생성) 시점에 파악하여 방지할 수 있다.
//Guestbook을 GuestbookDTO로 변환하는 코드
Function<Guestbook, GuestbookDTO> fn1 = (entity -> entityToDto(entity));
//동일 코드
Function<Guestbook, GuestbookDTO> fn2 =
new Function<Guestbook, GuestbookDTO>() {
@Override
public GuestbookDTO apply(Guestbook guestbook) {
return entityToDto(guestbook);
}
};
1개의 추상 메서드만 가지는 인터페이스를 함수형 인터페이스라고 한다. 대표적으로 Comparator 인터페이스가 있다.
위에서 사용된 Function은 함수형 인터페이스로 객체 생성 시 반드시 추상메서드 apply()를 구현해야 한다.
함수형 인터페이스는 추상메서드가 오로지 1개 이므로 굳이 메서드의 이름, 제네릭 타입 등 자세한 내용을 적어줄 필요가 없다.
그래서 함수형 인터페이스를 간단하게 축약해서 표현하기 위해 람다 표현식을 사용한다.
람다식의 기본 형태는 (parameter_list) -> { 추상메서드의 구현내용 }
다양한 사용 방법에 대해서는 여기 참고
Thymeleaf 템플릿 엔진을 사용하는 html에서 Tyhmeleaf 문법 오류가 있으면 500 에러가 발생하는 이유는?
서버에서 HTML(데이터가 반영된 Template)을 랜더링 하기 때문
Thymeleaf는 서버사이드 렌더링 환경에서 사용되는 템플릿 엔진으로 DB 혹은 API에서 가져온 데이터를 미리 정의된 Template에 넣어 html을 그려서 클라이언트에 전달해주는 역할을 한다.
JSP와 달리 servlet 코드로 변환되지 않으며 컨트롤러에서 값을 받아 HTML Text를 만들어 클라이언트(웹 브라우저)에 넘겨주는 역할만 담당한다.
추가로 Thymeleaf 템플릿 엔진을 사용하는 html 파일은 수정하면 바로 화면에 갱신되지 않는 이유도 서버에서 랜더링하기 때문이다. 서버사이드 랜더링은 컴파일을 해야(서버 재시작) 적용이 된다. 그래서 Spring에서는 spring-boot-devtools라는 의존성을 사용해서 개발한다.
Thymeleaf 동작 과정 참고
- 클라이언트의 요청을 받는다.
- 필요한 데이터(DB에서 가져오거나 API에서 가져오거나)를 가져온다.
- 미리 정의된 Template에 해당 데이터를 적절하게 넣는다.
- 서버에서 HTML(데이터가 반영된 Template)을 그린다.
- 해당 HTML을 클라이언트에 전달한다.
p203
BooleanBuilder는 querydsl 라이브러리에 있는거임?
웹 애플리케이션 제작 시 HttpServletRequest, HttpServletResponse를 Service 계층으로 전달하지 않는 것을 원칙으로 하듯이 엔티티 객체는 JPA 외에서 사용하지 않아야 한다.
따라서 Service 계층에서는 DTO로 파라미터와 리턴 타입을 처리하도록 구성해야 한다.
https://kkambi.tistory.com/30
http://egloos.zum.com/aeternum/v/1160846