멤버 컨트롤러가 멤버 서비스를 통해 회원가입과 데이터 조회를 할 수 있어야 함 (멤버 컨트롤러가 멤버 서비스를 의존한다)
main/java/hello.hellospring/controller -> New -> Class : MemberController.java 생성
@Controller 추가: Spring container에 MemberController 객체를 생성해서 spring에 넣어두고 관리함
@Autowired: 생성자에 @Autowired가 있으면 spring이 spring container에 있는 memberService와 연결시켜줌
생성자에 @Autowired가 있으면 스프링이 연관된 객체를 스프링 컨테이너에서 찾아서 넣어준다. 이렇게 객체 의존관계를 외부에서 넣어주는 것을 DI(Dependency Injection), 의존성 주입이라 한다.
이전 테스트에서는 개발자가 직접 주입했고, 여기서는 @Autowired에 의해 스프링이 주입해준다.
위까지 작성했을 때, memberservice 부분에 오류 발생
'hello.hellospring.service.MemberService' that could not be found.
Consider defining a bean of type 'hello.hellospring.service.MemberService' in your configuration.
memberService가 스프링 빈으로 등록되어 있지 않다.
(참고) IntelliJ 무료버전은 스프링에 대한 편의 기능을 지원하지 않아 오류로 인식하지 않았을 수 있음
(참고) helloController는 스프링이 제공하는 컨트롤러여서 스프링 빈으로 자동 등록된다.
@Controller가 있으면 자동 등록됨
스프링 빈을 등록하는 2가지 방법
컴포넌트 스캔과 자동 의존관계 설정 (4.1)
@Controller, @Service, @Repository
자바 코드로 직접 스프링 빈 등록하기 (4.2)
컴포넌트 스캔 원리
@Component 애노테이션이 있으면 스프링 빈으로 자동 등록된다.
@Controller 컨트롤러가 스프링 빈으로 자동 등록된 이유도 컴포넌트 스캔 때문이다.
@Component를 포함하는 다음 애노테이션도 스프링 빈으로 자동 등록된다.
@Controller
@Service
@Repository
@Controller, @Service, @Repository는 정형화된 표현
Controller를 통해 외부 요청을 받고, Service에서 비즈니스 로직을 만들고, Repository에서 데이터를 저장함
회원 서비스 스프링 빈 등록
MemberService.java의 class 위에@Service추가
(참고) 생성자에 @Autowired를 사용하면 객체 생성 시점에 스프링 컨테이너에서 해당 스프링 빈을 찾아서 주입한다. 생성자가 1개만 있으면 @Autowired는 생략할 수 있다.
회원 리포지토리 스프링 빈 등록
MemoryMemberRepository.java의 class 위에@Repository추가
스프링 빈 등록 이미지
memberService와 memberRepository가 스프링 컨테이너에 스프링 빈으로 등록되었다.
(참고) 스프링은 스프링 컨테이너에 스프링 빈을 등록할 때, 기본으로 싱글톤으로 등록한다(유일하게 하나만 등록해서 공유한다). 따라서 같은 스프링 빈으로 모두 같은 인스턴스다. 설정으로 싱글톤이 아니게 설정할 수 있지만, 특별한 경우를 제외하면 대부분 싱글톤을 사용한다.
DI (Dependency Injection, 의존관계 주입)
Controller와 Service 연결: @Autowired 사용
MemberController가 생성이 될 때, 스피링 빈에 등록되어 있는 MemberService 객체를 가져다 넣어줌
MemberService는 MemberRepository가 필요함
spring이 MemberService를 생성할 때, spring container에 등록하면서 public MemberService(...){...} 생성자를 호출
이때 @Autowired가 있으면, MemberRepository가 필요함을 알고 spring container에 있는 MemberRepository를 넣어줌
실행
HelloSpringApplication.java (main) 실행 -> 성공
Tomcat initialized with port(s): 8080 (http) 를 통해 성공적으로 실행됨을 알 수 있음
실행되는 파일(HelloSpringApplication.java)가 있는 패키지(hello.hellospring)의 하위 파일만 컴포넌트 스캔의 대상이 됨
4.2 자바 코드로 직접 스프링 빈 등록하기
회원 서비스와 회원 리포지토리의 @Service, @Repository, @Autowired 애노테이션을 제거하고 진행한다.
직접 설정 파일에 등록
main/java/hello.hellospring -> New -> Class : SpringConfig
SpringConfig.java
//SpringConfig.java
package hello.hellospring;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;
import hello.hellospring.service.MemberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpringConfig {
@Bean // spring Bean을 등록함
public MemberService memberService() {
return new MemberService(memberRepository()); //memberRepository를 엮어줌(넣어줌)
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
}
스프링 빈 등록 이미지가 완성됨
Controller는 @Controller, @Autowired 사용
(단축키) cmd + p : input을 확인할 수 있음
여기서는 향후 메모리 리포지토리를 다른 리포지토리로 변경할 예정이므로, 컴포넌트 스캔 방식 대신에 자바 코드로 스프링 빈을 설정하겠다.
(참고) XML로 설정하는 방식도 있지만 최근에는 잘 사용하지 않으므로 생략한다.
(참고) DI에는 필드 주입, setter 주입, 생성자 주입 이렇게 3가지 방법이 있다. 의존관계가 실행 중에 동적으로 변하는 경우는 거의 없으므로 생성자 주입을 권장한다.
위의 방법에서 생성자를 통해 들어옴: 생성자 주입 (가장 추천하는 방법)
필드 주입, setter 주입은 아래와 같음
필드 주입 단점: 중간에 바꿀 수 있는 방법이 없음
setter 주입 단점: public으로 열려있기 때문에 memberService.setMemberRepository()로 누구나 호출할 수 있음. 변경될 위험이 있음
(참고) 실무에서는 주로 정형화된 컨트롤러, 서비스, 리포지토리 같은 코드는 컴포넌트 스캔을 사용한다. 그리고 정형화되지 않거나, 상황에 따라 구현 클래스를 변경해야 하면 설정을 통해 스프링 빈으로 등록한다.
MemoryMemberRepository를 DBMemberRepository로 바꿀 때, @Bean 아래의 return new MemoryMemberRepository();만 return new DBMemberRepository();와 같이 바꿔주면 된다.
다른 코드 수정할 필요가 없다.
(주의) @Autowired를 통한 DI는 helloController, memberService 등과 같이 스프링이 관리하는 객체에서만 동작한다. 스프링 빈으로 등록하지 않고, 내가 직접 생성한 객체에서는 동작하지 않는다.
스프링 컨테이너, DI 관련된 자세한 내용은 스프링 핵심 원리 강의 참고
섹션 5. 회원 관리 예제 - 웹 MVC 개발
회원 웹 기능
홈 화면 추가 (5.1)
등록 (5.2)
조회 (5.3)
5.1 회원 웹 기능 - 홈 화면 추가
홈 컨트롤러 추가
main/java/hello.hellospring/controller -> New -> Class : HomeController.java 생성
HomeController.java
package hello.hellospring.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class HomeController {
@GetMapping("/")
public String home() {
return "home";
}
}
회원 관리용 홈
main/java/resources/templates -> New -> html : home.html 생성
main/java/hello.hellospring/controller : MemberForm.java 생성
SpringForm.java
package hello.hellospring.controller;
public class MemberForm {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
회원 컨트롤러에서 회원을 실제 등록하는 기능
MemberController.java
@PostMapping(value = "/members/new")
public String create(MemberForm form) {
Member member = new Member();
member.setName(form.getName());
memberService.join(member);
return "redirect:/";
}
@GetMapping: url 창에 입력. 조회할 때 주로 사용
@PostMapping: 데이터를 form에 넣어서 전달할 때 주로 사용. 값을 등록할 때 사용
같은 url이지만 @GetMapping과 @PostMapping에 따라 다르게 매핑 가능
실행
localhost:8080/member/new
5.3 회원 웹 기능 - 조회
회원 컨트롤러에서 조회 가능
MemberController.java
@GetMapping(value = "/members")
public String list(Model model) {
List<Member> members = memberService.findMembers();
model.addAttribute("members", members);
return "members/memberList";
}
회원 리스트 HTML
main/resources/templates/members : memberList.html 생성
댓글 영역