섹션 5. 스프링 빈과 의존관계
- 컴포넌트 스캔과 자동 의존관계 설정
회원가입 서비스 만들기 -> 멤버 컨트롤러가 멤버 서비스를 통해 회원가입을 해야 함. -> 멤버 컨트롤러가 멤버 서비스를 의존한다.
내용이 아무것도 없어도 @Controller 애노테이션이 있으면 멤버컨트롤러 객체를 만들어줌, 스프링이 관리하게 해줌
private final MemberService memberService = new MemberService();
원래 이렇게 선언했는데 차라리
이렇게 하는 것을 권장하신다. @Autowired 는 스프링 컨테이너에 있는 멤버 서비스를 연결해 준다.
다만 이렇게 하면 memberService에 빨간 줄이 뜨는데, 이 memberService는 순수한 자바 클래스이고, 스프링이 이를 알 수 없다.
그럴 때는 서비스 위에 @Service를 입력해준다. 이러면 스프링이 MemberService를 스프링 컨테이너에 등록해준다.
리포지토리는 @Repository를 써 준다. 이러면 이 역시 스프링이 리포지토리를 스프링 컨테이너에 등록해준다.
Controller - Service 연결 -> 멤버 컨트롤러 생성 시 컨테이너에 등록된 멤버 서비스 객체를 넣어준다 -> dependency injection
Service - Repository 연결 -> 위와 동일
엥 오류가 뜬다..
@Repository 를 MemberRepository 인터페이스에 하는 게 아니라 MemoryMemberRepository에다 하는 거였다..
스프링 빈을 등록하는 방법에는..
1. 컴포넌트 스캔과 자동 의존관계 (애노테이션 등록하는 방식) -> @Controller, @Service, @Repository를 들어가 보면 @Component라고 써있다. 위 애노테이션을 쓰면 스프링 객체를 생성해서 컨테이너에 올려준다. 그리고 @Autowired로 서로 연결해 준다. @Component는 애플리케이션을 포함한 하위 패키지에서 스캔하기 때문에, 패키지 밖에서 @Component를 쓰면 안된다.
2. 자바 코드로 직접 스프링 빈 등록하기 -> 다음 강의에서...
*스프링 빈 등록 시 싱글톤으로 등록한다. 같은 스프링빈이면 모두 같은 인스턴스이다.
-> 엥? 이게 뭔가 싶어서 구글링했다. 인프런 질문게시판에도 글이 있지만 말이 어려워서 쉬운걸로 가져왔다
- 자바 코드로 직접 스프링 빈 등록하기
Component스캔은 자동으로 하는 방법이고, 수동으로 빈 등록하는 방법이 있다.
MemberService와 MemoryMemberRepository에 @Repository, @Service, @Autowired를 지운다. MemberController는 그대로 둔다.
SpringConfig 파일을 만들어주고, 아래처럼 입력한다. 이러면 memberService를 빈으로 등록해 준다.
MemberService는 memberRepository를 필요로 하므로 memberRepository또한 등록해 준다.
그리고 MemberService에 memberRepository객체를 넣어주면 된다.
Controller는 안된다.
*DI(Dependency Injection (생성자를 통해서 컨트롤러, 서비스 등에 주입되는것) 는 세가지 종류가 있다.
- 필드주입은 생성자 없이 선언 시 바로 @Autowired하는 것이다. 나중에 변경이 어려워 좋진 않다.
- Setter주입은 Setter 안에 @Autowired하는 것이다. 호출당할 일이 있으면 public으로 설정되어야 하므로 좋진 않다.
- 생성자를 통한 주입을 권장한다.
**실무에서는 정형화된 코드(일반적으로 작성하는 컨트롤러,서비스,리포지토리)는 컴포넌트 스캔 방식을 이용한다.
반대로 정형화 되지 않거나, 상황에 따라 구현 클래스를 변경해야 하면, 설정을 통해 스프링 빈으로 등록한다.
현재 실습은 데이터 저장소가 없는 것으로 가정하고, 인터페이스와 구현체를 만들었다. 나중에 다른 리포지토리를 바꿀 때 코드변경 없이 하는 방법이 있다. 그걸 할려고 자바 코드로 수동으로 등록하는 거다. 리포지토리 변경시, 설정 파일의 코드만 바꾸면 된다.
***
스프링빈으로 등록이 안되어 있다면, @Autowired가 동작하지 않는다.
새롭게 직접 new 를 사용해서 객체를 생성하는 경우에도 @Autowired가 동작하지 않는다.
-> 스프링 빈으로 스프링 컨테이너에 올라간 것들만 @Autowired가 동작한다!
섹션 6. 회원 관리 예제 - 웹 MVC 개발
- 회원 웹 기능 - 홈 화면 추가
MemberController.java | resources\template\home.html | 구현 결과 |
위와 같이 코드를 작성해 준다.
여기서 스프링부트 애플리케이션 실행시 띄워주는 html 문서에도 우선순위가 있다.
요청이 오면 먼저 스프링 컨테이너 속 컨트롤러를 찾고, 없으면 static 파일을 찾는다.
Getmapping("/")과 home이 매핑 되어있으로 기존에 있던 index.html을 무시하고 home.html을 연결한다.
- 회원 웹 기능 - 등록
MemberController.java | templates\members\createMemberForm.html | 구현 결과 |
Form과 input 태그를 이용해서 등록 창을 만들었다.
MemberController.java | MemberForm.java |
이제 회원가입 컨트롤러를 만들어 보자. name을 선언하고, getter와 setter도 만들어 준다.
MemberController에서는 postmapping을 해 주고, member객체를 선언해 준다.
member.setName을 form입력창의 getName을 갖다 쓴다.
.join을 이용해 member 객체를 넘겨준 뒤, 홈 화면으로 리다이렉팅 시킨다.
회원 가입을 누르면 /members/new로 들어간다 (get방식)
-> createMemberForm으로 간다
-> createMemberForm으로 간다.
-> html이 띄워진다.
-> 정보 입력을 하고 제출하면, /members/new 에 Post 방식으로 넘어간다.
get과 post는 같은 url이지만 post를 호출하면 create 메소드로 들어간다. setName으로 값을 넣어준다.
- 회원 웹 기능 - 조회
MemberController.java | memberList.html | 구현 결과 |
${members} : list로 넘겨줬기 때문에, members안에는 회원 목록이 있음. 이를 루프를 돌면서 객체를 띄워준다.
정보가 메모리에 있기 떄문에, 서버를 재실행하면 회원 목록은 사라진다.
섹션 7.
- H2 데이터베이스 설치
실무에서는 데이터베이스에 데이터를 저장한다!
맥의 경우에는 사전에 권한을 부여해야 한다.
% cd Downloads\h2\bin
% chmod 755 h2.sh
% ./h2.sh
마지막줄을 입력하면
이런 화면이 뜬다.
처음으로 데이터베이스 파일을 만들어야 한다.
jdbc:h2:mem:testdb -> 내 파일 경로를 jdbc:h2:tcp://localhost/~/test 이렇게 바꿔준다
이러면 파일을 직접 접근하는 게 아니라 소켓을 통해 접근하게 된다.
어라...
사실 애초에 test.mv.db도 생성이 안 됐다. 그래서 그런가 보다.
JDBC URL을 jdbc:h2:~/test를 안했었다ㅋㅋㅋㅋㅋㅋ
다시보니 제대로 생겼다.
다시 jdbc:h2:tcp://localhost/~/test 로 바꿔 연결해 준다.
*문제가 있으면 rm test.mv.db 명령어로 데이터베이스를 지우고 처음부터 다시 하면 된다.
테이블을 하나 만들어준다.
테이블이 생기면 SELECT문도 가능하다.
*id가 java에서는 Long 인데 sql에서는 bigint이다.
INSERT문 | SELECT문 |
INSERT 문과 SELECT문이 잘 작동한다. Id를 포함하지 않고 INSERT를 해도 알아서 id값을 부여해 준다. (generated by default as identity덕분)
- 순수 JDBC
**참고만 하라고 하셨다.**
build.gradle에
두개의 라이브러리를 넣어준다. 코끼리가 안 뜨고, 옆의 사이드 바에도 없다면
build.gradle 우클릭 > link 로 시작하는 코끼리 아이콘의 옵션을 눌러준다.
그러면 이렇게 뜬다. 새로고침 해주면 된다!
접속 정보를 넣어줄 건데,
application.properties에 위 내용을 추가해 준다.
새 Repository를 만들고, option + return키를 눌러 메소드들을 가져온다.
에전엔 이런식으로 일일이 코드를 넣어줘야 했다....
h2를 실행시킨 상태로 실행하고, jpa라는 이름으로 회원을 등록해 보면, 위처럼 성공적으로 INSERT되었음을 확인할 수 있다.
객체지향 설계가 좋은 이유?
스프링 컨테이너가 인터페이스를 만들고, 다형성을 이용해 기존 코드의 변경 없이 구현체만 바꿔끼우는 것을 지원한다.
SOLID 원칙 중 OCP(open-closed principle) : 확장에는 열려있고, 수정/변경에는 닫혀있다.
-> 객체지향의 다형성이라는 개념을 활용하면 개방-폐쇄 원칙을 지킬 수 있다.
스프링의 DI 이용, 기존 코드 수정없이 설정만으로 구현 클래스 변경가능
- 스프링 통합 테스트
DB까지 연결한 테스트 구현하기 (통합 테스트)
기존의 MemberServiceTest를 복붙해서,
@SpringBootTest와 @Transactional 두 개를 추가해 주고,
기존 beforeEach와 afterEach 구문을 모두 삭제한 뒤,
@Autowired를 이용한다.
DB에는 transaction개념이 있다. commit 전까진 반영이 안된다.
@Transactional 애노테이션은 테스트 실행 시 transaction을 먼저 실행하고,
db에 데이터를 insert 한 뒤에,
테스트가 끝나면 rollback해준다(지워준다, 반영을 안한다).
즉, 다음 테스트를 반복수행할 수 있다. 테스트 데이터가 남지 않기 때문에..!!
@Springboot : 스프링 컨테이너와 테스트를 함께 실행
다만... 단위 테스트에 비해 통합 테스트가 시간이 오래 걸린다.
그래서 단위 테스트가 보통 좋은 테스트이다.
- 스프링 JdbcTemplate
JDBC API에서 본 반복 코드를 제거한 라이브러리이다. SQL은 직접 작성해야 한다.
생성자가 하나일 때는 @Autowired 생략 가능
- Jdbc Template으로 코드 줄임
- memberRowMapper 함수 lambda 함수로 간단하게 처리 가능
- TableName과 usingGeneratedKeyColums("id"); 로 INSERT문 만들 수 있음.
- Jdbc 템플릿으로 쿼리문 날림 -> 조회 간단히 가능
- 오류....
@Override
public Optional<Member> findById(Long id) {
List<Member> result = jdbcTemplate.query("select * from member where id = ?", memberRowMapper(), id);
return result.stream().findAny();
}
@Override
public Optional<Member> findByName(String name) {
List<Member> result = jdbcTemplate.query("select * from member where name = ?", memberRowMapper(), name);
return result.stream().findAny();
}
result의 파라미터가 하나 부족했다. 맨 뒤에 각각 id, name을 넣어줘야 한다.
- 야호
- JPA
JPA -> 객체를 DB에 따로 쿼리 없이 저장 가능.
JPA -> SQL과 데이터 중심 설계에서 객체 중심의 설계로 전환가능 -> 생산성 높일 수 있다!
build.gradle | application.properties |
설정 몇 가지를 추가해 준다.
JPA -> ORM(Object Relational Mapping) 기술 -> 어떻게? @Entity 애노테이션 이용
@Entity -> JPA가 관리한다.
DB가 Id를 자동 생성 -> identity 전략
JPA -> EntityManager가 관리함 -> JPA 쓰기 위해서는 이를 주입해야 함
- EntityManager 객체 생성
- save는 persist만 써 주면 저장 뿐만 아니라 setId기능까지 해 준다.
- findById 도 find() 를 써 준다.
- findByName과 findAll은 조금 다른데, JPQL로 쿼리문을 작성해야 한다.
JPQL은 객체에 쿼리를 날리는 건데, 이를 SQL문으로 해석해 준다.
즉, 조회 시 객체를 찾아버린다.
- findAll같은 경우에는 command + option + n을 누르면 in-line으로 변경 가능하다.
테스트 성공
- 스프링 데이터 JPA
섹션 8. AOP
- AOP가 필요한 상황
- AOP 적용
섹션 9. 다음으로
- 다음으로
'WINK-(Web & App) > Spring Boot 스터디' 카테고리의 다른 글
[2024 Spring Boot 스터디] 김아리 #2 주차 (0) | 2024.11.06 |
---|---|
[2024-2 SpringBoot 스터디] 조상혁 #2주차 (3) | 2024.11.06 |
[2024-2 Spring Boot 스터디] 백채린 #1주차 (0) | 2024.10.10 |
[2024-2 SpringBoot 스터디] 정호용 #1주차 섹션 1~4 (2) | 2024.10.10 |
[2024-2 Spring Boot 스터디] 류상우 #1주차 (0) | 2024.10.10 |