티스토리 뷰

728x90
반응형

스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 - 김영한 인프런 강의 참고

이전 포스팅 : 2023.01.01 - [Web Programming/Spring] - [Spring] 입문 - 스프링 빈과 컨테이너, 그리고 의존 관계(feat. IoC, DI)

지금까지 컨트롤러, 서비스, 리포지토리를 구현 및 테스트한 뒤 의존 관계까지 맺어줬다.

스프링 컨테이너 내에 등록된 스프링 빈 간의 의존 관계

이제 클라이언트가 서비스를 사용할 수 있도록 UI 페이지를 만들어야 한다. 템플릿 엔진으로 Thymeleaf를 사용하고 있기 때문에 Thymeleaf 문법을 활용한 html 페이지를 만든 뒤 컨트롤러와 연결시켜 주면 된다.

이전 포스팅에서 설명했던 MVC, 템플릿 엔진 이미지

회원 웹 기능으로는 간단하게 3가지만 구현한다.

  • 홈 화면 추가
  • 회원 등록
  • 회원 조회

홈 화면 추가


먼저 홈 컨트롤러를 추가해 주자.

…/controller/HomeController.java

package com.example.practice.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HomeController {
    
    @GetMapping("/")
    public String home() {
        return "home";
    }
}

이전 예제처럼 @Controller@GetMapping 어노테이션을 활용하면 된다.

/으로 GET 요청이 오면, home 이름의 html 파일을 /resources/templates/ 폴더에서 찾아 랜더링 후 리턴해 준다. 파일을 작성해 준다.

/resources/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> <!-- /container -->
</body>
</html>

  • 회원 가입 → /member/new로 GET 요청
  • 회원 목록 → /members로 GET 요청

회원 등록


회원 등록도 마찬가지로 컨트롤러 먼저 구현한 뒤, 그에 맞게 html 파일을 작성하면 된다.

먼저 클라이언트가 회원을 등록할 수 있도록 입력 창과 버튼이 있는 페이지를 만든다.

.../controller/MemberController.java

// ...

@Controller
public class MemberController {
    
    private final MemberService memberService;

    @Autowired
    public MemberController(MemberService memberService){
        this.memberService = memberService;
    }

    @GetMapping(value = "/members/new")
    public String createForm(){
        return "members/createMemberForm";
    }
}

/resources/templates/members/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> <!-- /container -->
</body>
</html>

input 태그에 name 속성 값이 추후 서버로 보내질 key 이름이 된다.

  • name 속성 값 : key
  • 클라이언트가 입력한 값 : value

클라이언트가 이름을 입력한 뒤 등록 버튼을 누르면 name 이라는 key에 입력한 값이 담겨 /members/new url로 POST 요청이 전송된다.

이제 /members/new를 POST로 처리할 컨트롤러를 구현해 준다.

스프링에서는 폼 데이터를 처리할 때, 각 name 속성 값에 대응되는 폼 클래스를 먼저 설계한 뒤, 폼 객체를 활용하여 POST 요청에 담긴 데이터를 가져온다.

.../controller/MemberForm.java

package com.example.practice.controller;

public class MemberForm {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

위와 같이 name 속성 값이 name이기 때문에 같은 이름을 가지는 인스턴스 변수를 타입에 맞게 private로 선언한다. 그 다음 각 변수의 getter, setter를 구현한다.

.../controller/MemberController.java

@Controller
public class MemberController {
    
    private final MemberService memberService;

    @Autowired
    public MemberController(MemberService memberService){
        this.memberService = memberService;
    }

    @GetMapping(value = "/members/new")
    public String createForm(){
        return "members/createMemberForm";
    }

    @PostMapping(value = "/members/new")
    public String join(MemberForm form){
        
        Member member = new Member();
        member.setName(form.getName());

        memberService.join(member);

        return "redirect:/";
    }
}

위와 같이 POST 요청은 @PostMapping 어노테이션을 사용한다.

그리고 매개변수로 위에서 구현한 MemberForm 타입 객체를 대입시켜 놓으면, 스프링이 자동으로 setName을 호출해 POST 요청으로 넘어온 데이터(클라이언트가 입력한 값)를 넣어준다.

그 후 MemberFormgetName으로 값을 가져와 Member 객체에 넣어줘 회원가입을 시킨다.

회원가입 후에는 /으로 리다이렉트시킨다.

회원 조회


마지막으로 가입한 회원들을 모두 조회하는 페이지를 구현한다.

.../controller/MemberController.java

@Controller
public class MemberController {
    
    private final MemberService memberService;

    // ...

    @GetMapping(value = "/members")
    public String list(Model model){

        List<Member> members = memberService.findMembers();
        model.addAttribute("members", members);
        
        return "/members/list";
    }
}

/resources/templates/members/list.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}">
                    <td th:text="${member.id}"></td>
                    <td th:text="${member.name}"></td>
                </tr>
            </tbody>
        </table>
    </div>
    </div> <!-- /container -->
</body>
</html>

thymeleaf 문법 중 each는 java의 forEach와 동일한 문법이라고 생각하면 된다.

Reference

728x90
반응형
댓글