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>
'개발 > Java' 카테고리의 다른 글
[Spring] Infearn 스프링 입문3 - (응용)MySQL & Mybatis 사용 (1) | 2022.12.15 |
---|---|
[Android 개발] Unresolved class 'Activity', xml에서 클래스를 못찾을 때 해결 (0) | 2022.12.07 |
[Spring] Infearn 스프링 입문 필기 1 - 개념 학습 (1) | 2022.12.06 |
[Android 개발] Todo List 메인 화면 제작 스타일 적용 (0) | 2022.12.06 |
[Android 개발] 안드로이드 RecyclerView(Adapter) 예제 코드 - Todo List 기본 틀 (0) | 2022.11.24 |