public interface QuestionRepository extends JpaRepository<Question, Integer> {
}
JpaRepository(스프링 프레임워크에서 제공하는 인터페이스)... 상속했음
상속할 때는 제네릭 타입으로 <Question, Integer>
repository 의 대상이 되는 엔티티의 타입(Question)과 해당 엔티티의 Primary Key의 속성 타입(Integer)을 지정해야함
테스트 코드 익숙해지기
@SpringBootTest 애노테이션
BasicApplicationTests 클래스가 스프링부트 테스트 클래스임을 의미
@Autowired 애노테이션
스프링의 DI 기능, questionRepository 객체를 스프링이 자동으로 생성해 준다.
@Autowired는 스프링 프레임워크에서 제공하는 의존성 주입(Dependency Injection)을 위한 애노테이션입니다. 이 애노테이션을 사용하면 스프링이 자동으로 의존성을 해결하여 해당 필드, 생성자, 메서드에 필요한 객체를 주입해줍니다.
@Repository의 메소드
대표적인것 몇가지
save(entity) | 엔티티를 저장하거나 수정 |
delete(entity) | 엔티티 삭제 |
findById(id) | 주어진 ID로 엔티티 조회 (리턴타입 optional) |
findAll() | 모든 엔티티 조회 |
count() | 엔티티의 총 개수 반환 |
existsById(id) | 주어진 ID의 엔티티가 존재하는지 확인 |
assertEquals(기대값, 실제값)
Junit의 메서드!!!!
기대값과 실제값이 동일하지 않다면 테스트는 실패로 처리
.get(int index) -> 해당 리스트에서 주어진 인덱스에 위치한 요소 가져옴
ex) a.get(0) -> a리스트의 첫번째요소
Optional<T> 클래스
Java 8부터 도입된 클래스로, 값의 존재 여부를 나타내는 컨테이너
optional의 메서드
.isPresent() | 값이 존재하는 경우 true, 객체가 비어있거나 값이 없는 경우 false (if문으로 많이 쓰인다, 값이 있으면 ~) |
.get() | Optional 객체 안의 값을 반환 -> 값이 존재하지 않을 경우 NoSuchElementException |
subject 값으로 데이터를 조회하기 위해.... -> findBySubject
그러나 findBySubject는 기본제공 메서드가 아니기 때문에
QuestionRepository 인터페이스에 추가해야 한다!
그럼 test에서 findBySubject 사용 가능
public interface QuestionRepository extends JpaRepository<Question, Integer> {
Question findBySubject(String subject);
앗! 그런데 인터페이스에 findBySubject 라는 메서드를 선언만 하고 구현은 하지 않았는데?
왜 되는거지??
JpaRepository를 상속했기 때문에 가능함!!!
DI에 의해 스프링이 자동으로 QuestionRepository 객체를 생성(이 때 프록시 패턴이 사용된다고 함)
findBy + 엔티티의 속성명(예:findBySubject)과 같은 레포지터리 메서드를 작성하면
해당 속성의 값으로 데이터를 조회 가능!
자주 쓰이는 조합들
and | findBySubjectAndContent(String subject, String content) | 여러 컬럼을 and로 검색 |
or | findBySubjectOrContent(String subject, String content) | 여러 컬럼을 or로 검색 |
between | findByCreateDateBetween(LocalDateTime fromDate, LocalDateTime toDate) | 컬럼을 between으로 검색 |
lessthan | findByIdLessThan(Integer id) | 작은항목 검색 |
like | findBySubjectLike(String subject) | like 검색 |
in | findBySubjectIn(String[] subjects) | 여러 값중에 하나인 항목 검색 |
orderby | findBySubjectOrderByCreateDateAsc(String subject) | 결과를 정렬하여 전달 |
응답 결과가 여러건인 경우에는 리포지터리 메서드의 리턴 타입을 Question이 아닌 List<Question> 으로 해야 한다.
테스트코드 작성시 참고
@RequiredArgsConstructor
Lombok에서 제공하는 애노테이션
클래스의 final 필드나 @NonNull이 붙은 필드에 대한 생성자를 자동으로 생성
여기에선 스프링 의존성 주입 규칙에 의해 questionRepository 객체가 자동으로 주입된다.
model
스프링에서 Model 객체는 컨트롤러(Controller)와 뷰(View) 사이에서 데이터를 전달하는 용도로 사용
컨트롤러는 비즈니스 로직을 처리하고 데이터를 Model 객체에 담아 뷰로 전달
Thymeleaf
타임리프에서 자주 사용되는 몇 가지 문법
- 조건문 (if 문):
- 사용법: th:if="${조건}"
- 설명: 조건이 참인 경우에만 해당 요소를 표시합니다.
- 예시: <div th:if="${question != null}">...</div>
- 반복문 (each 문):
- 사용법: th:each="변수 : ${컬렉션}"
- 설명: 컬렉션의 각 요소를 순회하며 해당 요소를 포함하는 엘리먼트를 반복하여 표시합니다.
- 예시: <ul th:each="question : ${questionList}"><li th:text="${question}">...</li></ul>
- 변수 선언 (let 문):
- 사용법: th:let="변수=${표현식}"
- 설명: 표현식을 평가하여 변수에 값을 할당합니다. 할당된 변수는 해당 엘리먼트 내에서 사용할 수 있습니다.
- 예시: <div th:let="count=${questionList.size()}">...</div>
- 속성값 대체 (attr 문):
- 사용법: th:attr="속성명=${표현식}"
- 설명: 속성명에 해당하는 요소의 속성값을 표현식으로 대체합니다.
- 예시: <img th:attr="src=@{images/${imageName}}">
- 텍스트 출력 (text 문):
- 사용법: th:text="${표현식}"
- 설명: 표현식의 값을 해당 요소의 텍스트로 출력합니다.
- 예시: <span th:text="${question.subject}">...</span>
출처: chatgpt
참고 https://wikidocs.net/161186
Service가 필요한 이유
모듈화
보안: 컨트롤러는 리포지터리 없이 서비스를 통해서만 데이터베이스에 접근하도록 구현하는 것이 안전
엔티티 - DTO 변환 역할: 엔티티클래스는 DB와 직접 맞닿아 있는 클래스이기 때문에 컨트롤러나 템플릿 엔진에 전달하여 사용하는 것은 비추 -> DTO 필요!
Controller -> Service -> Repository 구조로 데이터를 처리
타임리프에서 th:href 처럼 URL 주소를 나타낼때는 @{ ~~ } 사용
문자열(/question/detail/)과 ${question.id}와 같은 자바 객체의 값을 더할 때는 | ~~ | 기호로 좌우를 감싼다
<a th:href="@{|/question/detail/${question.id}|}" th:text="${question.subject}"></a>
DataNotFoundException.java
이게 뭐라고 클래스까지 만들지? 싶지만...
RuntimeException 상속
@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "entity not found") 이것도 붙여야 작동한다