본문 바로가기
개발/Java

[Spring] Infearn 스프링 입문 필기 2 - 실습, 회원 등록, 회원 조회 기능 만들기

by m_.9m 2022. 12. 6.

1. index.html 작성

1에서 세팅해놓은 환경설정을 바탕으로 기본 html 화면을 띄웠을 때의 화면이다.

 

2. Template Engine - thymeleaf

:freemarker. Groovy. thymeleaf, Mustache 주로 사용

JBLHtmlToThymeleaf 를 셋팅에서 플러그인 해주고 html내에서 사용해보겠다.

자바 아래 컨트롤러라는 패키지와 hello Controller 클래스 추가, templates에 html 작성.

hello Controller.java

package hello.hellospring.Controller;
import org.springframework.ui.Model;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HelloController {

    @GetMapping("hello")
    public String hello(Model model){
        model.addAttribute("data","Hello!!");
        return "hello";
    }
}

hello.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>hello</title>
  <link href="style.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<p th:text="'안녕하세요' + ${data}"> 안녕하세요 </p>
</body>
</html>

 

 

2.1 정적 컨텐츠

static내 정적 컨텐츠를 그대로 실행시켜주는 것이 정적 컨텐츠의 기능이다.

 

 

2.2 파라미터 받아보기

helloController.java 내용 ++

@GetMapping("hello-mvc")
    public String helloMVC(@RequestParam(value = "name", required = false) String name, Model model){
        model.addAttribute("name", name);
        return "hello-template";
    }

 

hello-template.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>hello</title>
  <link href="style.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<p th:text="'hello' + ${name}"> hello! empty </p>
</body>
</html>

 

 

실행 화면

 

 

2.3 문자로 그냥 내려주기

 

helloController.java 내용 ++

@GetMapping("hello-string")
    @ResponseBody
    public String helloString(@RequestParam("name") String name){
        return "hello" + name;
    }

 

2.4 API 형식

helloController.java 내용 ++

@GetMapping("hello-api")
    @ResponseBody
    public Hello helloApi(@RequestParam("name") String name){
        Hello hello = new Hello();
        hello.setName(name);
        return hello;
    }

    static class Hello{
        private String name;

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

    }

private 인 걸 get/set 등의 메소드를 통하여 접근한다.

 

 

3. 백엔드 설정

비지니스 요구사항 정리

  • 데이터 : 회원 ID, 이름
  • 기능: 회원 등록, 조회
  • 회원 리포지토리 케이스 작성

 

 

3.1 MemberRepository

domain package > Member.java에서

id, name 만들고 getter/setter를 설정한다.

package hello.hellospring.domain;

public class Member {

    private Long id;
    private String name;
)

 

 

repository에 MemberRepository.java를 작성한다.

package hello.hellospring.repository;
import hello.hellospring.domain.Member;

import java.util.List;
import java.util.Optional;

public interface MemberRepository {

    Member save(Member member);
    Optional<Member> findbyid(Long id);
    Optional<Member> findbyName(String name);
    List<Member> findAll();

}

 

 

MemoryMemberRepository.java를 작성한다.

package hello.hellospring.repository;

import hello.hellospring.domain.Member;

import java.util.*;

public class MemoryMemberRepository implements MemberRepository{

    private static Map<Long, Member> store = new HashMap<>();
    private static long sequence = 0L;

    @Override
    public Member save(Member member) {
        member.setId(++sequence);
        store.put(member.getId(), member);
        return member;
    }

    @Override
    public Optional<Member> findbyid(Long id) {
        return Optional.ofNullable(store.get(id));
    }

    @Override
    public Optional<Member> findbyName(String name) {
        return store.values().stream()
                .filter(member -> member.getName().equals(name))
                .findAny();
       }

     @Override
    public List<Member> findAll() {
        return new ArrayList<>(store.values());
    }

    public void clearStore() {
            store.clear();
    }
}

 

 

Test에서 만들어진 repository를 검증한다.

아래 초록불 ✅로 세개의 테스트가 모두 정상 동작하는 것을 확인한다.

 

 

package hello.hellospring.repository;

import hello.hellospring.domain.Member;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;

import java.util.List;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;

public class MemoryMemberRepositoryTest {

    MemoryMemberRepository repository = new MemoryMemberRepository();
    @AfterEach
    public void afterEach(){
        repository.clearStore();
    }

    @Test
    public void save(){
        Member member = new Member();
        member.setName("spring");
        repository.save(member);

        Member result = repository.findbyid(member.getId()).get();
        //assertEquals(member, result);
        assertThat(member).isEqualTo(result);
    }

    @Test
    public void findByName(){
        Member member1 = new Member();
        member1.setName("spring1");
        repository.save(member1);

        Member member2 = new Member();
        member2.setName("spring2");
        repository.save(member2); //Shift + F6 한꺼번에 리네임이 가능

        Member result = repository.findbyName("spring1").get();
        assertThat(result).isEqualTo(member1);
        //테스트를 하나 끝내면 깔끔하게 클리어를 해줘야함.
    }

    @Test
    public void findAll(){
        Member member1 = new Member();
        member1.setName("spring1");
        repository.save(member1);

        Member member2 = new Member();
        member2.setName("spring2");
        repository.save(member2);

        List<Member> result = repository.findAll();
        assertThat(result.size()).isEqualTo(2);
    }

}

 

 

 

3.2 MemberServiceRepository

MemberService 생성

package hello.hellospring.service;

import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemberRepository;

import java.util.List;
import java.util.Optional;

public class MemberService {
    //Ctrl + Shift + T : 새 테스트 생성

    private final MemberRepository memberRepository;

    public MemberService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

    /**
     * 회원가임
     */

    Long join(Member member){

        validateDuplicateMember(member); //중복 회원 관련
        //Ctrl + AIT + Shift + T : Refactor 관련
        memberRepository.save(member);
        return member.getId();

    }

    private void validateDuplicateMember(Member member) {
        memberRepository.findbyName(member.getName())
        //Ctrl + AIT + V : 변수 추출하기
                .ifPresent(m ->{
                throw new IllegalStateException("이미 존재하는 회원입니다.");
        });
    }

    /**
     * 전체 회원 조회
     */
    public List<Member> findMembers(){
     return memberRepository.findAll();
    }

    public Optional<Member> findOne(Long memberId){
        return memberRepository.findbyid(memberId);
    }
}

 

 

MemberServiceTest 생성

아래 초록불 ✅로 네개의 테스트가 모두 정상 동작하는 것을 확인한다.

 

 

package hello.hellospring.service;

import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemoryMemberRepository;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;

class MemberServiceTest {

    MemberService memberService;
    MemoryMemberRepository memberRepository;

    @BeforeEach
    public void beforeEach(){
        memberRepository = new MemoryMemberRepository();
        memberService = new MemberService(memberRepository);
    }

    @AfterEach
    public void afterEach(){
        memberRepository.clearStore();
    }

    @Test
    void join(){
        //given
        Member member = new Member();
        member.setName("spring");
        // when
        Long saveId = memberService.join(member);

        //then
        Member findMember = memberService.findOne(saveId).get();
        Assertions.assertThat(member.getName()).isEqualTo(findMember.getName());
    }

    @Test
    public void 중복_회원_예외() {
        //given
        Member member1 = new Member();
        member1.setName("spring");

        Member member2 = new Member();
        member2.setName("spring");

        //When
        memberService.join(member1);
        IllegalStateException e =assertThrows(IllegalStateException.class, () -> memberService.join(member2));
assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
    }

    @Test
    void findMembers() {
    }

    @Test
    void findOne() {
    }

}

 

 

 

3.3 스프링 빈과 의존 관계

스트링 빈을 등록하는 두가지 방법

  • 컴포넌트 스캔과 자동 의존 관계 설정
    • @Controller, @Service, @Autowired
    • 컨테이너를 만들시 @controller로 컨트롤러임을 알리고@Autowired 로 컨트롤러와 서비스에 명시해 리포지토리까지 연결.
    • 클래스를 찾을때나 리포지토리를 찾을 떄 참고할 수 있게 @로 알림.
  • 자바 코드로 직접 스프링 빈 등록하기
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());
    }

    @Bean
    public MemberRepository memberRepository(){
        return new MemoryMemberRepository();
    }
}

* DI에는 생성자 주입, 필드 주입, setter 주입 이렇게 3가지 방법이 있다. 의존 관계가 실행 중 동적으로 변경되는 경우는 거의 없어 생성자 주입을 권장한다.

* 실무에선 컴포넌트 스캔을 사용하지만 상황에 따라 구현 클래스를 변경해야하면 스프링빈을 직접 등록해야한다.

 

 

4. 프론트엔드 설정

4.1 메인화면

HomeController.java 생성

package hello.hellospring.Controller;

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

@Controller
public class HomeController {

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

 

 

Home.html 생성

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Main</title>
</head>
<body>

<div class="container">
  <div>
    <h1> Hello Spring </h1>
    <p> 회원 기능</p>
      <a href="/member/new"> 회원 가입</a>
      <a href="/member"> 회원 목록</a>
  </div>
</div>

</body>
</html>

 

 

 

4.2 회원 등록

MemberController.java

package hello.hellospring.Controller;

import hello.hellospring.domain.Member;
import hello.hellospring.service.MemberService;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import java.util.List;

@Controller
public class MemberController {

    private final MemberService memberService;

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

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

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

        System.out.println("member ="+member.getName());

        memberService.join(member);

        return "redirect:/home";
    }

    @GetMapping("/members")
    public String list(Model model){
        List<Member> members = memberService.findMembers();
        model.addAttribute("members", members);
        return "members/memberlist";
    }
}

 

 

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;
    }
}

 

 

createMemberForm.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>회원 가입</title>
</head>
<body>

  <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>

</body>
</html>

 

 

 

4.2 회원 리스트 조회

memberlist.html 생성 - Thyleaf 문법 ${}.

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thyleaf.org">
<head>
    <meta charset="UTF-8">
    <title>회원 목록</title>
</head>
<body>
  <div class="container">
    <table>
      <thead>
        <tr>#</tr>
        <tr>이름</tr>
      </thead>
      <tbody>
        <tr th:each="member: ${members}">
          <td th:text="${member.id}"></td>
          <td th:text="${member.name}"></td>
        </tr>
      </tbody>
    </table>
  </div>
</body>
</html>