아래 내용은
김영한님의 Spring 핵심 원리 - 기본편의 일부를 정리한 내용입니다
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8
View 작동 우선순위:
1. 스프링 컨테이너에 올라와져 있는 컨트롤러에서 랜더링할 페이지를 찾는다.
2. 찾고 없으면, static/home.html 을 띄워준다.
DB(H2) 와 JPA
Intellij 코드 수정
build.gradle 파일에 jpa, h2 데이터베이스 관련 라이브러리 추가
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'com.h2database:h2'
스프링 부트 데이터베이스 연결 설정 추가
resources/application.properties
spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.jpa.show-sql=true
// jpa는 User객체만 보고, 자동으로 테이블도 다 만드는데 이 자동을 막아주고 시작
spring.jpa.hibernate.ddl-auto=none
주의!: 스프링부트 2.4부터는 spring.datasource.username=sa 를 꼭 추가해주어야 한다. 그렇지
않으면 Wrong user name or password 오류가 발생한다. 참고로 다음과 같이 마지막에 공백이
들어가면 같은 오류가 발생한다. spring.datasource.username=sa 공백 주의, 공백은 모두
제거해야 한다.
JPA
JPA: 자바 표준 인터페이스
구현은 hibernate로 한다 (eclipse.. 등도 있다)
쉽게 말해서, 인터페이스: JPA, 구현체: hibernate 를 쓸 거다!!
JPA는 ORM(Object Relational Mapping) 기술이다.
- 객체와 Relational Database 테이블을 매핑 시키는 기술이다!!!
- 인터페이스를 통한 기본적인 CRUD는 제공한다
- `findByName` 과 같은 메서드(인터페이스) 이름만으로도 조회 기능 가능
- 페이징 기능 가능
build.gradle에 jpa를 써준덕분에, 스프링이 현재 데이터베이스와 연결까지 다 해서 EntityManger를 만들어서 만들어준다. 그걸 주입받으면 된다.
Entity 수정
@Entity
public class User {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
}
SpringConfig
- DI 받는게 하나 밖에 없을 땐, @Autowired 생략 가능
package day1.day1spring;
import day1.day1spring.repository.JpaUserRepository;
import day1.day1spring.repository.MemoryUserRepository;
import day1.day1spring.repository.UserRepository;
import day1.day1spring.service.UserService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.persistence.EntityManager;
@Configuration
public class SpringConfig {
private EntityManager em;
@Autowired
public SpringConfig(EntityManager em) {
this.em = em;
}
@Bean
public UserService userService(){
return new UserService(userRepository());
}
@Bean
public UserRepository userRepository(){
// return new MemotryUserRepository();
return new JpaUserRepository(em);
}
}
JpaUserRepository.java
- MemoryUserRepository 랑 똑같이 UserReposiotry를 상속받는다.
package day1.day1spring.repository;
import day1.day1spring.domain.User;
import javax.persistence.EntityManager;
import java.util.List;
import java.util.Optional;
public class JpaUserRepository implements UserRepository{
private final EntityManager em;
public JpaUserRepository(EntityManager em) {
this.em = em;
}
@Override
public User save(User user) {
em.persist(user);
return user;
}
@Override
public Optional<User> findById(Long id) {
User user = em.find(User.class, id);
return Optional.ofNullable(user);
}
@Override
public Optional<User> findByName(String name) {
List<User> result = em.createQuery("select u from User u where u.name=:name", User.class)
.setParameter("name", name)
.getResultList();
return result.stream().findAny();
}
@Override
public List<User> findAll() {
return em.createQuery("select u from User u", User.class)
.getResultList();
}
}
JPA는 모든 데이터가 들어올 때, Transcation 안에서 실행되야 하므로, JPA를 쓰려면 데이터를 저장하거나 변경할 때 항상 `@Transaction` 이 필요하다. 따라서, Service 계층(JpaRepository를 쓰는) 에 `@Transaction` 을 적어준다.
- `@Transaction`. : 테스트 케이스에 이 어노테이션이 있으면, 테스트 실행 전에 트랜잭션을 시작하고 테스트 완료 후에 항상 롤백해버린다. 따라서, DB에 데이터를 반영되지 않고 작동되는 작동되므로 다음 테스트에 영향을 미치지 않음
UserServiceIntegrationTest.java
@SpringBootTest
@Transactional
public class UserServiceIntegrationTest {
@Autowired UserService userService;
@Autowired UserRepository userRepository;
@Test
void 회원가입() {
//given
User user = new User();
user.setName("정윤수");
//when
Long joinId = userService.join(user);
//then
User result = userService.findByOne(joinId).orElse(null);
assertThat(result.getName()).isEqualTo(user.getName());
}
@Test
void 유저_중복_확인(){
User user1 = new User();
user1.setName("정윤수");
User user2 = new User();
user2.setName("정윤수");
//when
userService.join(user1);
IllegalStateException e = assertThrows(IllegalStateException.class, () -> userService.join(user2));
//then
assertThat(e.getMessage()).isEqualTo("중복된 회원 이름 입니다.");
}
@Test
void findAllUser() {
}
@Test
void findByOne() {
}
}
SpringDataJpaRepository 사용
- SpringDataJpaRepository 가 JPA 기술 가져다 쓰는건데, 더 편리하게 쓸 수 있게 해주는 기술이다.
SpringDataJpaRepository.java
- SpringDataJpaRepository 도 결국은 인터페이스다.
- JpaRepository가 인터페이스에 대한 구현체를 지 알아서 만들어내고, 스프링 빈에 등록해놓는다.
- findByName 과 같은 인터페이스만 알고 쓰더라도 JPQL (select u from User u where u.name = ? ) 과 같은 쿼리를 자동으로 만들어준다.
public interface SpringDataJpaRepository extends JpaRepository<User, Long>, UserRepository{
@Override
Optional<User> findByName(String name);
}
SpringConfig
- JpaRepository가 스프링 빈에 등록해놓은 덕분에 Injection 받음
@Configuration
public class SpringConfig {
// 기존의 코드 주석 처리
// private EntityManager em;
// @Autowired
// public SpringConfig(EntityManager em) {
// this.em = em;
// }
private final UserRepository userRepository;
public SpringConfig(UserRepository userRepository) {
this.userRepository = userRepository;
}
// @Bean
// public UserService userService(){
// return new UserService(userRepository);
// }
// @Bean
// public UserRepository userRepository(){
//
// return new JpaUserRepository(em);
// }
}
```
인터페이스 란
- 위 그림에서, 인터페이스는 역할 을 나타낸다. 역할만 알면 그 구현을 어떤 것을 하더라도(구현에 대해서 내부적인 원리를 몰라도) 운전을 할 수 있다 는 것이다.
즉, 인터페이스는 자바에서 클래스들이 구현해야하는 동작(자동차 구현) 을 지정하는 용도로 사용되는 추상 자료형(자동차 역할) 이다.
또 다른 예로는, 집에 스위치를 누를 때, 뭐 어떻게 내부에서 동작하는진(전선, 전자의 흐름..) 모르고 그냥 버튼(인터페이스) 기능(껐다,키는)을 쓸 수 있는 형태
이 버튼이 가지고 있는 기능을 다른 곳에서도 여러번 주입 받아서 또 쓸 수 있다.
이 버튼 자체는 바꿀 수 없지만(변경에 대해서는 닫혀있는), 큰 저택에는 좀 더 고급스러운 터치식 버튼(껐다키는), 좁은 집에는 아날로그 식 버튼(껐다키는) 과 같은 해당 규격에 맞는 클래스를 생성해서 버튼을 주입받을 수 있는 (확장에는 용이) 한 것이다.
또 다시 인터페이스는 자바에서 클래스들(각지각곳의 버튼들)이 구현해야하는 동작을 지정하는 용도로 사용되는 추상 자료형(버튼객체)이다.
개방 폐쇄 원칙(OCP: Open-Closed Principle) : 확장에는 열려있고, 수정에는 닫혀있다.
스프링의 DI(Dependencies injection)을 사용 -> 기존 코드를 전혀 손대지 않고, 설정만으로 구현 클래스를 변경할 수 있음
자바 코드에서 살펴보면, 인터페이스를 하나에 대해서 사용하기만 하면 그 구현체가 무엇(MemoryMemberReposiotry 든 JdbcMemberRepository)이든 간에 그 구현체의 내부적인 원리를 모르고도 MemberRepository 라는 인터페이스를 통해 얼마든지 쓸 수 있다는 것이다.
'인프런 강의' 카테고리의 다른 글
스프링 빈 주입 (0) | 2023.07.05 |
---|---|
WAS에서 화면을 랜더링?? (0) | 2023.06.30 |
WAS의 Servlet (feat. Multi Thread) (0) | 2023.06.29 |
Web Server와 WAS (0) | 2023.06.28 |
Web Server와 WAS (0) | 2023.06.23 |