Section 4. 스프링 빈과 의존관계
서비스를 통해 리포지토리에 회원을 저장할 수 있게 되었음!
이제 화면을 붙이고 싶은데
→ 컨트롤러와 뷰템플릿이 필요함!!
MemberController 가 서비스를 통해서 회원가입과 조회를 할 수 있어야함!!
→ 이런 것을 의존관계 라고 한다.
MemberController 를 생성한다!
package hello.hellospring.controller;
import hello.hellospring.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 어노테이션을 달아놓으면 스프링이 실행될 객체를 생성해서 들고있음
→ 스프링 부트안에 스프링 컨테이너가 있고 이 스프링 컨테이너 안에 Controller 객체를 생성해서 가지고 있다!!
스프링 컨테이너에 memberService 를 등록해놓고 공용으로 써야함!
→ memberService 를 굳이 여러개의 Controller 에서 각각 new 를 해서 사용하면 너무 비효율적
생성자를 만들고 @Autowired 어노테이션을 달아서 스프링이 스프링 컨테이너에 있는 MemberService 를 가져와서 연결시켜줌
근데 열심히 코드를 작성하고 실행시켜보니 오류가 발생!! 왜???
MemberService 는 순수한 자바파일이기 때문에 스프링이 얘가 무슨 역할을 하는지 알 수 가 없음!!
→ 따라서 @Service 어노테이션을 통해 스프링에게 알려줘야 스프링이 올라올 때 얘는 Service 구나! 를 알고 스프링 컨테이너에게 MemberService 를 등록해줌!!
→ 마찬가지로 MemoryMemberRepository 에는 @Repository 어노테이션을 달아줘야한다!
컨트롤러를 통해서 외부 요청을 받고
서비스에서 비즈니스 로직을 만들고
리포지토리에서 데이터를 저장하는
정형화된 패턴임!
helloController 와 memberService 와 memberRepository 를 차례로 연결시켜줘야 함!!
→ @Autowired 를 사용하여 MemberController 가 생성이 될 때 스프링 빈에 등록되어있는 MemberService 객체를 가져와서 넣어줌
→ MemberService 를 가져오려고 가보니 역시 @Autowired 가 되어 있으므로 memberRepository 가 필요하다는 것을 알게 됨!
→ MemRepository 는 구현체로 MemoryMemberRepository 를 가지고 있으므로 이것도 넣어줌!
이렇게 하면 helloController 와 memberService 와 memberRepository 를 연결시킬 수 있음
메인 메서드 실행을 해보면 잘 뜨는걸 확인할 수 있음!!
→ 스프링이 컨테이너를 만들 때 문제가 없었다는 것을 확인
스프링 빈을 등록하는 2가지 방법
- 컴포넌트 스캔과 자동 의존관계 설정
→ @Service @Controller @Repository 등의 방식을 사용하는 것!
(이것들은 @Component 의 한 종류임!!)
→ 스프링이 올라올 때 @Component 와 관련된 어노테이션이 있으면 각각 객체를 생성하여 스프링 컨테이너에 등록함
→ @Autowired 는 이러한 것들의 연관관계를 나타내줌
- 자바 코드로 직접 스프링 빈 등록하기
우선 MemberController 파일을 제외한 나머지 파일들의 @Service @Repository @Autowired 를 지워준다!
그 다음에 hello.hellospring 안에 SpringConfig 라는 파일을 만들어준다
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
public MemberService memberService() {
return new MemberService(memberRepository());
//MemberService 는 memberRepository 와 엮어줘야함
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
//memberRepository 는 인터페이스이므로
// 구조체인 MemoryMemberRepository 를 new 해줌
}
}
이렇게 작성해주면 스프링이 @Configuration 을 보고 들어가서 @Bean 밑의 코드를 읽어 스프링 빈에 등록해준다
잘 실행되는걸 확인할 수 있음!!
💡 스프링은 스프링 컨테이너에 스프링 빈을 등록할 때, 기본으로 싱글톤으로 등록한다!! (유일하게 하나만 등록해서 공유한다.)
→ 따라서 같은 스프링 빈이면 모두 같은 인스턴스다.
→ 설정으로 싱글톤이 아니게 설정할 수 있지만 특별한 경우를 제외하면 대부분 싱글톤을 사용한다.
💡 DI(Dependency Injection) 는 컴포넌트들 간의 의존성을 외부에서 주입해주는 것으로 필드 주입, setter 주입, 생성자 주입 이렇게 3가지 방식이 있지만 의존관계가 실행중에 동적으로 변하는 경우는 거의 없으므로 생성자 주입을 권장한다!!
💡 실무에서는 주로 정형화된 컨트롤러, 서비스, 리포지토리 같은 코드는 컴포넌트 스캔을 사용한다. 그리고 정형화되지 않거나, 상황에 따라 구현 클래스를 변경해야 하면 설정을 통해 스프링 빈으로 등록한다!
💡 @Autowired 를 통한 DI 는 helloController , MemberService 등과 같이 스프링이 관리하는 객체에서만 동작한다. 스프링 빈으로 등록하지 않고 내가 직접 생성한 객체에서는 동작하지 않는다.
→ 스프링 컨테이너에 올라가는 것들만 @Autowired 를 사용할 수 있음!
Section 5. 회원 관리 예제 - 웹 MVC 개발
회원 웹 기능 - 홈 화면 추가
홈 화면을 추가하기 위해 HomeController 를 만들어준다!!
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";
}
}
전에 배웠던 대로 GetMapping("/") 은 처음 접속했을 때의 도메인을 매핑해주는 것이고!
return “home” 이므로 templates 폴더 안에 home.html 을 만들어준다!!
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div class="container">
<div>
<h1>Hello Spring</h1>
<P>회원 기능</P>
<p>
<a href="/members/new">회원 가입</a>
<a href="/members">회원 목록</a>
</p>
</div>
</div>
</body>
</html>
서버를 켜보면
요렇게 잘 나오는걸 확인할 수 있다
그럼 여기서 생기는 궁금증이 있을텐데~~
index.html 로 만든 Welcom Page 는 어떻게 되는걸까???
→ 우선 순위에 따라 home.html 이 화면에 보여지게 된다
→ 예전에 배웠듯이 스프링은 스프링 컨테이너에서 Controller 를 먼저 확인해보는데 HomeController 에 @GetMapping("/") 가 있으므로 return “home” 에 의해 home.html 이 보여지게 되는 것이다!!
→ 만약 HomeController 가 없다면 그대로 index.html 이 Welcom Page 로 사용될 것이다!!
회원 웹 기능 - 등록
localhost:8080 에 접속했을 때 회원가입을 누르면 members/new 도메인으로 이동했었다!!
→ 근데 안 만들었으니까 당연히 오류가…
이제 만들어보자!!
MemberController 에 다음의 내용을 추가해준다
...
@GetMapping("/members/new")
public String createForm() {
return "members/createMemberForm";
}
//url 창에 도메인을 입력하는 것은 보통 GetMapping
코드를 살펴보면 @GetMapping 에서 return 이 members/createMemberForm 이므로
templates 폴더에 members 라는 package 를 생성 후 createMemberForm.html 을 생성해준다!!
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div class = "container">
<form action = "/members/new" method = post>
<div class="form-group">
<label for="name">이름</label>
<input type = "text" id = "name" name = "name" placeholder="이름을 입력하세요">
</div>
<button type = "submit">등록</button>
</form>
</div>
</body>
</html>
이제 서버를 키고 회원가입을 눌러보면 사진처럼 잘 나오는걸 볼 수 있다!!
이제 회원등록을 하는 껍데기를 만들었으니 컨트롤러를 만들어보자~~
Controller 폴더안에 MemberForm.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 에 다음의 내용을 추가해준다!
...
@PostMapping("/members/new")
public String create(MemberForm form) {
Member member = new Member();
member.setName(form.getName());
memberService.join(member);
return "redirect:/"; //회원 가입이 끝났으므로 홈 화면으로 돌려보냄
...
@PostMapping이라는 새로운 어노테이션이 등장했다!!
→ 이것은 post 방식을 의미함
→ data 를 html의 form 같은데 넣어서 전달할 때는 post를 사용!!
→ get은 조회할 때 주로 사용!!
💡 Get, Post Mapping 모두 /members/new 인데 차이점이 무엇일까?!
처음 화면에서 회원가입을 직접 클릭해서 들어갈 때는 Get 방식으로 Mapping 되고 회원가입 화면에서 등록 버튼을 누르면 form 에서 설정해놨던 Post 방식으로 Mapping 되어서 실행된다!
회원 웹 기능 - 조회
회원 목록을 클릭하면 localhost:8080/members 로 이동해야한다!!
그렇다면 이제 Mapping 해야 된다는 것쯤은 직감이 오죠..?
MemberController 에 다음의 내용을 추가한다!!
@GetMapping("/members")
public String list(Model model) {
List<Member> members = memberService.findMembers();
model.addAttribute("members", members);
return "members/memberList";
}
return 을 members/memberList 해줬으므로!
templates 폴더안에 members/memberList.html 을 만들어준다!
<!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}">
<!-- controller 에서 넘겨준 members 를 each 문법 (반복문)을 사용해서 하나씩 접근함 -->
<td th:text="${member.id}"></td>
<td th:text="${member.name}"></td>
<!-- 반복문을 돌며 하나씩 로직이 실행되는데 getter 를 사용하여 private 변수인 id 와 name
의 값을 읽어옴-->
</tr>
</tbody>
</table>
</div>
</div>
</body>
</html>
이렇게 작성한 후에~
서버를 킨 다음 회원가입에서 아무거나 적고 등록한 후에 회원 목록을 확인해 보면 이렇게 추가되어 있는 걸 확인할 수 있다!!
💡 메모리에 있기 때문에 서버를 내렸다가 다시 키게 되면 데이터가 전부 지워짐!!
→ 회원 데이터가 사라진 것을 확인할 수 있음 (DB의 필요성!!)
'WINK-(Web & App) > Spring Boot 스터디' 카테고리의 다른 글
[2024 Spring Boot 스터디] 정성원 #1 주차 - 객체 지향 설계와 Spring (0) | 2024.05.11 |
---|---|
[Spring Boot 스터디] 류건 #3 주차 - 섹션 4, 5 (0) | 2023.07.30 |
[Spring Boot 스터디] 이지원 #3 주차 - 섹션 4, 5 (0) | 2023.07.27 |
[Spring Boot 스터디] 조현상 #3 주차 - 섹션 4,5 (0) | 2023.07.27 |
[Spring Boot 스터디] 류건 #2주차 - 섹션 3 "횐님 관리 예제" (1) | 2023.07.21 |