기본적으로 Service가 Repository에게 의존하는 것처럼, Controller는 Service에게 의존한다.
방법은 두가지가 있다. (1) 각각의 어노테이션과 autowired를 활용하여 묶어주는 법이 있고, (2) 직접 스프링 빈에 입력해주는 법이 있다.
(1) annotation & autowired를 활용하기
어노테이션이 있으면 해당 녀석을 스프링이 먼저 불러올 수 있다 - "컴포넌트 스캔 & 의존성 주입"
정확히 말하면, 스프링은 어노테이션이 붙은 클래스들을 스프링 컨테이너에서 관리하는 Bean으로 등록한다. 그리고 Spring은 해당 빈들을 Dependency Injection으로 관리한다.
이때 의존성을 주입하는 방법은 여러가지가 있는데, 기본적으로는 @Autowired를 필수적으로 사용한다고 생각하면 된다.
다만 '생성자 주입'을 사용한다면, 생성자가 하나인 경우에 한해 @Autowired 없이도 진행할 수 있다.
1 생성자 주입
@Controller
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
}
2 필드 주입
@Controller
public class UserController {
@Autowired
private UserService userService;
}
3 세터 주입
@Controller
public class UserController {
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
}
-> 그냥 생성자 주입을 주로 쓴다고 생각하면 된다.
-> @Autowired가 필요없는 경우는 '생성자 주입' 시 '생성자가 하나일 때' 밖에 없다. 저 경우가 잦기 때문에, @Autowired를 안 써도 된다고 오해할 수 있지만, 기본적으로 @Autowired를 쓴다고 생각하면 된다. 다만 초기에 스프링이 자바빈을 싱글톤으로 등록하기 때문에, 여러 개의 생성자가 있는 경우에는 가장 많은 파라미터를 가진 아이에게만 @Autowired를 붙여주고, 해당 녀석이 싱글톤으로 등록이 된다.
@Autowired라는 어노테이션은, 스프링 컨테이너에서 멤버 서비스를 가져오는 건데, 가져오는 대상 또한 어노테이션이 있어줘야 한다. 기존에 우리가 만든 부분에서, 없는 어노테이션을 추가해줬다. Service에 @Service를 넣어주고, Repository에도 @Repository를 넣어준다.
(2) 스캐닝이 아닌, 직접 스프링 빈에 추가해주기
우선 service, repository의 어노테이션을 다 지워준다.
그러고 SpringConfig라는 클래스를 만들어서, 다음과 같은 코드를 짜준다.
이러면 Bean을 직접 만들어주게 되는 것이다.
뭐가 낫지? 둘 중에?
두 개가 각각 장단점이 있다.
1 컴포넌트 스캐닝 + 의존성 주입: 장점: 자동화로 인해 편리하고, 유지보수하고, Convention이므로 이해하기 쉽다.
2 스프링 빈에 직접 추가: 장점: 기존의 컨벤션 외의 빈을 만들 때 필수고, 자동화가 아니므로 성능은 빠름.
파일을 모아두므로 한눈에 보기 쉬울 것 같기도 함.
=> 그래서 결론적으로는, 컴포넌트 스캐닝 + 의존성 주입을 기본으로 하되, 필요에 따라 직접 추가도 해주기로 한다.
MVC 모델 관련 - 회원 관리 예제
▶ 홈 화면
우선 homeController를 만들고, '/'에 대한 매핑을 해줬다.
이때 중요한 것이, 컨트롤러가 정적 파일보다 우선순위가 높다.
즉, 원래 기본형은 resources > index.html 인데, 컨트롤러 중에 해당 URI가 있는지 여부를 먼저 확인하기 때문에 /가 매핑되어 있다면 해당 페이지를 먼저 보여주고 index.html은 무시하면 된다.
일련의 과정이 지난 후에 redirect 시켜주고 싶을 땐 redirect:/ 이렇게 해주는 것 잊지 말고!
▶ 회원가입 화면
유저가 입력한 값을 보면, HTML의 form 에 입력하고 button을 누르면 submit이 된다.
그때 form에 action을 보면 /members/new 이렇게 되어있다.
그걸 통해서 해당 부분으로 넘어가면 controller 중에 그 부분에 포함된 mapping을 따라 간다.
그러면 거기서 form을 받아서 그 값으로 member라는 새로운 객체를 만든 이후 그걸 memberService에 넣어주면 된다.
▶ 회원 조회 화면
/members라는 uri에 대해서, 넘어오는 값을 Model이라는 곳의 attribute에 넣어준다.
나중에 HTML에 thymeleaf를 쓰게 되면, 거기서 th가 등장하게 되고, each를 써주면 자동으로 model에 접근을 해서 (${members}) 해당 모델의 attribute인 members를 하나씩 건드리면서 member를 반환한다.
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div class="container">
<div>
<table>
<thead>
<tr>
<th>#</th>
<th>이름</th>
</tr>
</thead>
<tbody>
<tr th:each="member : ${members}">
<td th:text="${member.id}"></td>
<td th:text="${member.name}"></td>
</tr>
</tbody>
</table>
</div>
</div> <!-- /container -->
</body>
</html>
=> 전반적인 플로우는 어렵지가 않다. 다만, 어떤 경우에 Service, Repository 등을 써야하는지,
그리고 우리가 DB가 아닌 Memory라는 걸 따로 만들어서 관리해주고 있기 때문에 이 부분도 약간 의아하다.
이제는 메모리가 아니라 DB를 직접 다뤄보자.
'백엔드 잡학사전' 카테고리의 다른 글
[스프링 입문] 스프링에서의 AOP (0) | 2024.08.11 |
---|---|
[스프링 입문] 스프링에서의 DB 접근 기술 (0) | 2024.08.11 |
[스프링 입문] 회원 관리 예제의 백엔드 개발 & 의존성 주입 (0) | 2024.08.10 |
[스프링 입문] 정적 컨텐츠와 MVC, 템플릿 엔진 (0) | 2024.08.03 |
[스프링 입문] 스프링 관련 기본 개념들 (0) | 2024.08.02 |