Controller, Service, Repository 계층 간의 이동에 주목!
- 컨트롤러는 사용자의 요청을 받아들이고 그에 대한 응답을 주는 계층
- 서비스는 핵심 로직을 작동하는 계층
- 레포지토리는 데이터베이스와 응답하고 결과값을 Service계층에 전달하는 역할
- 이 계층을 구현하는 방법은 클래스에 각 어노테이션을 다는 것.
이 3가지 어노테이션 모두 컴포넌트 어노테이션이 내장되어 있어 싱글톤 객체를 구현한다!
Controller 계층
@Controller
@RequestMapping("/member")
public class MeberController {
의존성주입(Dependency Injection) 방법1. Autowired 어노테이션 사용 : 필드 주입
@Autowired -->**싱글톤 객체를 여기 컨트롤러로 가지고 온다.**
private MemberService memberService;
// 홈화면
@GetMapping("")
public String memberHome(){
return "member/home";
}
// 회원 목록 조회
@GetMapping("/list")
public String memberList(Model model){
// MemberListRes는 멤버 조회용 객체
// 멤버를 조회해서 모델 매개체를 통해 mvc패턴으로 프론트에 쏴줘야하는데 컨트롤러에서 직접 멤버를 조회하는게 아니라 서비스 계층에서!
// 그런데 문제는 멤버서비스 서비스 어노테이션이 붙으면서 싱글톤 객체를 가지고 있으므로 싱글톤 객체를 여기 컨트롤러 클래스에 가지고 와야함
List<MemberListRes> memberListResList = memberService.findAll();
model.addAttribute("memberList",memberListResList);
return "member/member-list";
}
**-->컨트롤러 계층에서는 객체를 바로 다루지 않는다! 응답과 요청을 위한 서브 객체(클래스)를
만들어 사용한다. 여기서도 회원 목록 조회를 구현할 때 멤버 객체를 가지고 와서 model객체에 addAttribute
해서 mvc패턴으로 프론트에 쏴주는 게 아니다!**
**-->멤버 객체에는 모든 속성(비밀번호같은 정보 포함)을 다 조회할 수 있기 때문에
멤버 조회용 객체(ex.이름,이메일만을 속성값으로 갖는)를 따로 만든다.**
**-->그리고 멤버조회라는 비즈니스 로직은 컨트롤러 계층이 아닌 서비스 계층에서 구현하기 때문에
서비스 계층에 매서드를 구현하고 서비스객체를 불러 메서드를 호출한다.**
**-->이 때! 서비스클래스는 @서비스 어노테이션이 붙어 싱글톤 객체를 가진다. 따라서 싱글톤 객체를
여기 컨트롤러 계층에 가져오기 위해 의존성 주입을 해야하고 이는 AutoWired어노테이션을 통해 구현할 수 있다.**
// 회원 상세 조회(name,email,password 조회되게)-->id값 넘겨박아서 id값 출력
@GetMapping("/detail/{id}")
public String memberDetail(@PathVariable long id){
System.out.println(id);
return "member/member-detail";
}
// 회원가입(name,email,password)=>MemberCreateDto
@GetMapping("/create")
public String memberCreate(){
return "member/member-create";
}
**-->사용자가 회원가입할 데이터를 post할 html화면을 리턴한다.**
// 새로운 화면 리턴이 아닌 url 재호출을 통해 redirect
@PostMapping("/create")
public String memberCreatePost(@ModelAttribute MemberCreateDto mcd){
memberService.save(mcd);
return "redirect:/member/list"; //redirect는 html화면을 직접 리턴하는 것이 아니라 url링크가 Getmapping된 메서드를 리턴
}
}
**-->사용자가 데이터를 post하면 postMapping된 메서드로 부터 데이터를 처리하는데 여기서는
파라미터 형식으로 들어와 데이터바인딩으로 객체로 받았다.**
**-->여기서도 멤버 객체가 아니라 회원가입용 멤버 객체(클래스)를 미리 만들어 받고,
이를 최종적으로 레포지토리에 전달해야하기 위해 그 전 단계인 서비스 계층에 전달하기 위해
매개변수로 넣어 서비스 계층에 전달한다.**
Service 계층
@Service
public class MemberService {
@Autowired
private MemberMemoryRepository memberMemoryRepository;
public List<MemberListRes> findAll(){
List<Member> members= memberMemoryRepository.findAll();
List<MemberListRes> memberListRes= new ArrayList<>();
for(int i=0; i<members.size(); i++){
Long id = members.get(i).getId();
String name = members.get(i).getName();
String email = members.get(i).getEmail();
MemberListRes m1 = new MemberListRes(id,name,email);
memberListRes.add(m1);
}
return memberListRes ;
}
**-->회원 목록 조회 메서드는 컨트롤러 계층에서 MemberListRes라는 조회용 객체를 따로 만들어
모델 객체에 추가하여 mvc패턴으로 사용자에게 전달한다.**
**-->따라서, 서비스 계층에서 이 로직을 구현한 다음 컨트롤러 계층에 전달해야하므로 리턴값이
MemberListRes 조회용 객체인 것이다.**
**-->그런데 또 서비스 계층에서는 멤버 조회를 하려면 데이터베이스와 소통하는 레포지토리 계층으로부터
객체를 가지고 와야한다. 그런데 레포지토리 계층은 데이터베이스와 직접 연결되어 객체를 주고 받으므로,
서비스 계층은 레포지토리 계층으로부터 Member객체를 전달 받는 것이다.
-->이때 레포지토리 계층으로부터 멤버 객체를 전달받기위해 레포지토리에서 객체를 전달하는 메서드를
호출해야하는데, 레포지토리도 싱글톤 객체이므로 여기 서비스 계층에 레포지토리 객체를 가지고 오기 위해서
의존성 주입. 여기서는 AutoWired어노테이션을 통해 의존성 주입을 하였다.**
**-->따라서, 이 Member객체에서 조회용 객체에 필요한 속성값만 뽑아내 조립하여 리턴하는 것이다.**
public void save(MemberCreateDto memberCreateDto){
Member member = new Member(MemberMemoryRepository.id,memberCreateDto.getName(),
memberCreateDto.getEmail(), memberCreateDto.getPassword());
memberMemoryRepository.save(member);
}
}
**-->컨트롤러 계층으로부터 dto객체를 전달받았고 서비스 계층은 이를 다시 레포지토리 계층에 전달해야한다.
레포지토리는 데이터베이스에 순수한 객체를 저장해야하므로 서비스계층에서 dto객체를 진짜 member객체로 보완하여
전달한다. 따라서, dto객체의 속성값에 id같은 속성값을 추가해서 보완하여 Model객체로 조립한다음
레포지토리 계층의 메서드를 호출하여 전달한다.**
Repository 계층
@Repository //레포지토리도 안에 컴포넌트 어노테이션이 내장->싱글톤 객체
public class MemberMemoryRepository {
private List<Member> memberList = new ArrayList<>();
public static Long id=1L;
**-->원래 레포지토리는 데이터베이스에서 멤버리스트를 가지고 오겠지만 오늘 수업은
레포지토리가 데이터를 가지고 있는 것으로 대체**
public List<Member> findAll(){
return this.memberList;
}
**-->서비스 계층에서 호출하는 findAll메서드. db와 직접 연결되는 레포지토리에서는 멤버 객체를
리턴한다.**
public void save(Member member){
this.memberList.add(member);
id++;
}
}
**-->서비스 계층에서 member객체를 조립하여 받아야하므로 매개변수에 Member객체를 받고
이를 데이터베이스에 저장한다.
여기서는 직접 db와 연결된 것이 아니라 id++같은 조치를 취했지만 실제로는 db에 넣으면
auto_increment가 되니 실제로는 스프링에서 id++할 필요가 없다.**
정리
- 결국 컨트롤러 계층은 사용자의 요청을 받거나 응답하기 위한 전용 객체(DTO)를 다룬다
- 레포지토리 계층은 가장 앞선에서 데이터베이스에 데이터를 저장하거나 받는 계층으로 진짜 객체를 다룬다.
- 중간에 위치한 서비스 계층은 레포지토리로부터 본연의 객체를 전달받아 컨트롤러 계층에 전용객체를 리턴해주거나, 컨트롤러 계층으로부터 전용객체를 전달받아 본연의 객체로 만들어 레포지토리 계층에 전달한다.
- 여기서는 회원가입을 위해 데이터를 받는 상황에서, 컨트롤러 계층에서 프론트로부터 사용자에게 직접 데이터를 전달받고 이를 서비스 계층의 save메서드를 호출하여 서비스 계층에 전달한다.
그리고 서비스 계층은 컨트롤러에게 전달받은 데이터를 레포지토리의 save메서드를 호출하여 레포지토리 계층에 전달한다.
최종적으로 레포지토리 계층의 save메서드는 서비스계층으로부터 데이터를 전달받아 데이터베이스에 저장한다.
- 사용자가 회원 목록 조회를 요청하면 컨트롤러 계층은 서비스 계층의 findAll메서드를 호출하여 전용 객체를 전달받는다. 서비스계층의 findAll메서드는 레포지토리계층의 findAll메서드를 호출하는데 이는 본연의 객체를 리턴한다. 이로 인해, 객체를 전달받은 서비스계층은 본연의 객체를 목록조회용 객체로 다듬어 컨트롤러 계층에 리턴하는 것이다.
- 그리고 각각 다른 계층의 메서드를 호출하는 과정에서는, 컨트롤러,서비스,레포지토리 어노테이션 모두 컴포넌트 어노테이션을 내장하여 싱글톤 객체를 구현하므로 다른 계층의 싱글톤 객체를 현재 위치한 계층에 불러오기 위해 의존성 주입이라는 작업을 해야한다. 오늘 수업은 그 의존성 주입의 방법으로 AutoWired어노테이션에 대해 배웠다.