회원 관리 예제를 웹 mvc를 통해 개발해보겠습니다.
- 회원 웹 기능 - 홈 화면 추가
- 회원 웹 기능 - 등록
- 회원 웹 기능- 조회
1. 회원 웹 기능 - 홈 화면 추가
이전 시간까지 열심히 만들어 놓은 멤버 컨트롤러를 통해 회원을 등록하고 조회하는 기능을 만들어보겠습니다.
먼저 아주 단순한 홈 화면을 추가해봅시다.
HomeController를 생성하고 localhost:8080에 home.html을 호출했습니다.
그리고 이렇게 단순한 html을 생성한 뒤 실행하면
잘 뜨네용
그런데 이상한 점이 있습니다.
뭘까용??
분명 이전에 static으로 index.html을 welcome page로 만들어 줬는데 뜨지 않네요.
그 이유는 우선 순위 때문입니다.
welcome page와 같은 정적 컨텐츠는 스프링 컨테이너에서 컨트롤러를 먼저 실행시키기 때문입니다.
그래서 컨트롤러를 통해 home.html을 홈화면으로 설정해주었으므로 index.html은 나오지 않게 됩니다.
2. 회원 웹 기능 - 등록
이제 회원 가입이 되도록 해봅시다.
회원 가입 버튼을 누르면 "/members/new"로 이동하기 때문에 MemberController에서 같은 url로 매핑시켰습니다.
그리고 createMemberForm.html을 리턴했죠?
그럼 만듭시다.
우선 이렇게 html을 작성하고 실행시키면
이름을 입력할 수 있도록 화면은 설정이 되었고 등록 버튼을 누르면 아직 아무런 기능을 하지 못 하고 있습니다.
input으로 받는 name="name"은 서버에 넘어올 때 key가 됩니다.
이름을 입력한 뒤 등록 버튼을 누르면 form 태그에 있는 action url인 "/members/new" 주소에 post 방식으로 이름이 넘어갑니다.
이제 껍데기(?)는 다 만들었기 때문에 회원이 실질적으로 등록되는 컨트롤을 작성해보겠습니다.
MemberForm에 우선 name을 생성했습니다. 이 name은 위의 createMemberForm.html에서 input으로 받는 name="name"과 매칭될 것 입니다.
여기서는 @GetMapping이 아닌 @PostMapping입니다.
createMemberForm.html에서 값을 넘길 때 form 태그에서 post 방식으로 넘겼기 때문에 @PostMapping을 사용합니다.
url창에 주소를 직접 입력하는 것은 getMapping이었습니다.
postMapping은 보통 데이터를 form같은 곳에 넣어서 전달할 때 사용하고 get은 조회할 때 주로 사용합니다.
따라서 위의 코드처럼 get이냐 post냐에 따라 같은 url이어도 다르게 mapping할 수 있습니다.
form 태그에서 post 방식으로 넘겼기 때문에 @PostMapping이 선택되면서 아래의 create 메소드가 호출됩니다.
이때 메소드에 들어오는 값은 위에 작성한 MemberForm의 name이 됩니다. html에서 입력받은 name이죠?
이를 getName으로 가져오고 Member 객체에 setName으로 넣어준 다음 memberService의 join메서드를 통해서 회원 가입을 시킵니다.
그리고 마지막 return 주소는 redirect를 사용해 홈 화면으로 돌렸습니다.
3. 회원 웹 기능 - 조회
이제 정말 마지막입니다.
힘겹게 회원 가입시킨 것들이 제대로 되었는지 조회 기능을 통해 살펴봅시다.
조회 버튼을 눌렀을 때 url은 "/members"였습니다. getMapping을 시켜주고
list 메소드에는 Model 객체를 가져옵니다.
만들어 두었던 memberService.findMembers 메소드를 사용하면 모든 회원 리스트가 반환됩니다.
이렇게 반환된 members를 addAttribute 해줍니다. (model 객체를 통해 뷰 템플릿에 넘깁니다.(뷰에 데이터 전달))
이제 memberList.html을 만들면 되겠죠?
위와 같이 작성했습니다.
thymeleaf 문법을 사용했는데 우선 th:each를 사용하면 모델 객체로 넘겨준 members (회원 리스트)를 루프로 돌게 됩니다.
각각 회원인 member를 통해 id와 name을 가져왔습니다.
따라서 이름을 입력하고 조회를 눌러보면 위와 같이 잘 반영됩니다.
서버를 종료하고 다시 실행하면 기존에 작성한 멤버들은 모두 초기화됩니다.
우리는 메모리를 사용했기 때문이죠.
따라서 이러한 데이터들을 db에 저장해야 합니다.
4. 스프링 통합 테스트
DB가 있다는 전제 하에
DB connection 정보도 스프링 부트가 들고 있고(?) 하는 상황이라 순수 자바 코드로는 테스트 진행X
따라서 테스트를 스프링과 엮어서 해야 합니다.
이렇게 MemberServiceTest를 복사해서 MemberServiceIntegrationTest를 생성했습니다.
(에러가 뜨는 것은 DB를 만들지 않았기 때문입니다.)
우선 이전에는 BeforeEach에서 직접 객체를 생성해서 넣어줬다면
스프링 컨테이너에서 멤버 서비스, 멤버 레포지토리를 가져오는 형태로 만들어줍니다.
테스트는 가장 끝단이므로 가장 편한 방법을 쓰자!
@Autowired를 바로 써주면 된다~
이때 memberRepository는 MemeoryMemerRepository에서 MemerRepository로 바꿔줘야 한다.
(테스트 케이스에는 실제 구현체가 아닌 인터페이스를 써줘야 한다네요..)
@SpringBootTest : 스프링 컨테이너와 테스트를 함께 실행
@Transactional : 테스트 케이스에 이 어노테이션이 있으면, 테스트 시작 전에 transcation 시작하고, 테스트 완료된 후 항상 롤백한다. 이렇게 하면 DB에 데이터가 남아있지 않기 때문에 다음 테스트에 영향 주지 않는다.
(transaction을 시작한다 > 데이터베이스 작업을 수행하기 위한 일종의 '작업 단위'를 생성하는 것 > 테스트 메서드가 실행되는 동안에만 트랜잭션 내에서(작원 단위 내에서) 데이터베이스 작업을 수행)
순수 자바 코드 테스트 : 일종의 단위 테스트. 시간 매우 적게 걸림
통합 테스트 : 스프링 컨테이너, DB 연결하는 테스트. 시간 상대적으로 걸림