본문 바로가기

WINK-(Web & App)/Spring Boot 스터디

[Spring Boot 스터디] 한준교 #3 주차 - 섹션 4, 5

반응형

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의 필요성!!)

반응형