지금까지 단순한 객체를 통한 서비스 개발 및 테스트를 통한 검증을 하였는데 이제 서비스 화면을 백엔드 시야에서 어떻게 처리하는지 알아본다.
Controller와 ViewTemplate을 이용한다.
1. MemberController를 생성할 건데, Controller가 memberService를 통해 서비스가 이루어지고 데이터를 조회할 수 있어야한다. → MemberController와 MemberService의 의존관계 설정
-> 이것을 Spring을 통해 구현
가장 먼저 MemberController를 구현할건데
package Hello.HelloBus.controller;
import Hello.HelloBus.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class MemberController {
private final MemberService memberService;
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
}
위와 같이 구현할 수 있다.
가장 먼저 @Controller 어노테이션을 통해 스프링 컨테이너에 MemberController를 등록해준다. 그리고 MemberService를 이용하기 위해 받아주어야하는데 전 게시물에서 사용했던 것처럼 new를 통해 새로운 MemberService를 생성하는 것이 아닌 생성자를 통해 주입 받았다.
이는 스프링을 이용하게 되면 모두 스프링에 객체를 저장하고 사용하고자 하는 것은 스프링 컨테이너로부터 받아서 사용해야하기 때문이다.
@Autowired 어노테이션은 뭘까?
일단 위에서 말한 것처럼 MemberController는 @Controller 어노테이션을 통해 스프링 컨테이너에 등록을 해준다. 그럼 그 때 생성자를 호출하는데 생성자에 @Autowired가 있으면 생성자 매개변수 MemberService를 스프링이 스프링 컨테이너에 있는 MemberService와 연결해준다.
그럼 위 말은 스프링 컨테이너에 MemberService가 등록이 되어있다는 뜻인데 전 게시물에서 MemberService와 MemberRepository를 스프링 컨테이너에 등록해준 적이 없다. 따라서 위와 같이 MemberController만 스프링 컨테이너에 등록시켜주고 위와 같이 사용하려하면 당연히 제대로 이루어질 수 없다.
MemberService와 MemberRepository를 스프링 컨테이너에 등록 시켜준 후 사용해야한다.
package Hello.HelloBus.service;
import Hello.HelloBus.domain.Member;
import Hello.HelloBus.repository.MemberRepository;
import Hello.HelloBus.repository.MemoryMemberRepository;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
@Service
public class MemberService {
private final MemberRepository memberRepository;
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
/* 회원가입 서비스 */
public Long join(Member member){
// 같은 이름이 존재하는 중복 회원 생성 x
validateDuplicateMember(member);
memberRepository.save(member);
return member.getId();
}
private void validateDuplicateMember(Member member) {
memberRepository.findByName(member.getName())
.ifPresent(m->{
throw new IllegalStateException("이미 존재하는 회원입니다.");
});
}
/* 회원 조회 서비스 */
public List<Member> findMembers(){
return memberRepository.findAll();
}
public Optional<Member> findOne(Long memberId){
return memberRepository.findById(memberId);
}
}
package Hello.HelloBus.repository;
import Hello.HelloBus.domain.Member;
import org.springframework.stereotype.Repository;
import java.util.*;
@Repository
public class MemoryMemberRepository implements MemberRepository{
private static Map<Long, Member> store = new HashMap<>();
private static long sequence = 0L;
@Override
public Member save(Member member) {
member.setId(++sequence);
store.put(member.getId(), member);
return member;
}
@Override
public Optional<Member> findById(Long id) {
// id로 얻는 것인데 만약 존재하지 않는 경우 Null을 반환
// 따라서 Optional로 반환
return Optional.ofNullable(store.get(id));
}
@Override
public Optional<Member> findByName(String name) {
return store.values().stream()
.filter(member->member.getName().equals(name))
.findAny();
}
@Override
public List<Member> findAll() {
return new ArrayList<>(store.values());
}
public void clearStore(){
store.clear();
}
}
위 처럼 MemberService Class에는 @Service 어노테이션을,
MemberRepository에는 @Repository,
MemberController에는 @Controller를 통해 스프링 컨테이너에 등록을 해준 후 사용을 한다. 이는 현재 실무의 표준이라고 말할 수 있을 정도로 정형화되어있다고 한다.
위 처럼 3개의 객체가 모두 스프링 컨테이너에 등록이 되어있다. 이를 서로 연결시켜주는 것이
아까 처음에 말한 @Autowired 어노테이션이다. 그리고 이것이 Dependancy Injection 방식이다.
(각 클래스에서 다른 생성자를 호출 할 경우 무조건 @Autowired를 통해 스프링으로부터 받아야한다)
이 방법을 "컴포넌트 스캔과 자동 의존관계 설정" 이라고 표현한다. 우리가 사용한건 @Service, @Controller, @Repository인데 왜 컴포넌트라 하냐? 3가지 어노테이션 모두 컴포넌트 어노테이션을 포함하고 있기 때문이다.
그리고 컴포넌트 스캔과 자동 의존관계 설정을 허용하는 범위는 우리가 사용하고자 하는 main 클래스의 package만 포함한다.
참고: 스프링은 스프링 컨테이너에 스프링 빈을 등록할 때, 기본으로 싱글톤으로 등록한다(유일하게 하나만 등록해서 공유한다) 따라서 같은 스프링 빈이면 모두 같은 인스턴스다. 설정으로 싱글톤이 아니게 설정할 수 있지만, 특별한 경우를 제외하면 대부분 싱글톤을 사용한다
'Spring > Spring Core Basic' 카테고리의 다른 글
좋은 객체 지향 프로그래밍 (0) | 2022.01.21 |
---|---|
자바 코드로 직접 스프링 빈 등록하기 (0) | 2022.01.06 |
회원 서비스 테스트 (0) | 2022.01.05 |
회원 서비스 개발 (회원 가입, 회원 조회) (0) | 2022.01.05 |
회원 Repository 및 TestCase 작성 (0) | 2022.01.05 |