본문 바로가기

백엔드 잡학사전

[스프링 입문] 스프링 빈과 의존관계 & MVC 개발하기 예시

기본적으로 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를 직접 다뤄보자.