회원가입과 가입된 회원조회를 위한 간단한 사이트를 만들어보자
목적은 3가지로
- MVC 패턴의 흐름 파악
- 스프링 빈을 등록하고 교체하는 방법
- 테스트코드 작성 방법
이것에 대해 확인 할 수 있다.

새로운 프로젝트를 만들어 원하는 위치에 압축을 푼다
컨트롤러를 관리할 패키지를 만들고 메인에 접속할 HomeController를 만들어 주자

@Controller
public class HomeController {
@GetMapping("/")
public String home() {
return "home";
}
}
home.html도 만들자

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<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>
중간중간 실행 시켜 잘 되는지 확인도 해보자

회원가입 시 필요한 정보를 담을 도메인을 만들자

public class Member {
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
데이터에 접근하는 Repository를 만들자
유연한 교체를 위해 인터페이스를 생성하자
public interface MemberRepository {
Member save(Member member);
Optional<Member> findById(Long id);
Optional<Member> findByName(String name);
List<Member> findAll();
}
구현하자
public class MemoryMemberRepository implements MemberRepository{
private final 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());
}
// test 코드를 위한 메소드
public void clearStore() { store.clear(); }
}
데이터베이스는 다음장에서 연결해보고 여기선 메모리를 사용하여 확인해보자
테스트코드를 작성하자

// org.junit.jupiter.api.Assertions 이 클래스도 테스트 때 사용되는 클래스이다.
import org.assertj.core.api.Assertions;
class MemoryMemberRepositoryTest {
MemoryMemberRepository repository = new MemoryMemberRepository();
// 각 테스트 후 실행 할 동작
@AfterEach
public void afterEach() {
repository.clearStore(); // 메모리 정리
}
@Test
void save() {
//given
Member member = new Member();
member.setName("spring");
//when
Member result = repository.save(member);
//then
Assertions.assertThat(member).isEqualTo(result);
}
@Test
void findById() {
// given
Member member = new Member();
member.setName("spring1");
repository.save(member);
// when
Member result = repository.findById(member.getId()).get();
// then
Assertions.assertThat(member).isEqualTo(result);
}
@Test
void findByName() {
// given
Member member = new Member();
member.setName("string");
repository.save(member);
// when
Member result = repository.findByName(member.getName()).get();
// then
Assertions.assertThat(member).isEqualTo((result));
}
@Test
void findAll() {
// given
Member member1 = new Member();
member1.setName("spring1");
repository.save(member1);
Member member2 = new Member();
member2.setName("spring2");
repository.save(member2);
Member member3 = new Member();
member3.setName("spring3");
repository.save(member3);
// when
List<Member> result = repository.findAll();
// then
Assertions.assertThat(result).contains(member1, member2, member3);
}
}
- given : 테스트의 필요한 변수, 값을 준비
- when : 테스트의 대상이 되는 동작 수행
- then : 테스트의 결과를
- @BeforeEach : 각 테스트 전에 공통적으로 수행되는 내용
- @AfterEach : 각 테스트 후에 공통적으로 수행되는 내용
기능에 해당하는 Service를 작성하자

public class MemberService {
private final MemberRepository memberRepository;
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
// 회원가입
public Long join(Member member) {
// 같은 이름의 회원 가입X
validateDuplicateMember(member);
memberRepository.save(member);
return member.getId();
}
private void validateDuplicateMember(Member member) {
memberRepository.findByName(member.getName())
.ifPresent(m -> {
throw new IllegalStateException("Already Existence : " + m.getName());
});
}
// 전체 회원 조회
public List<Member> findMembers() {
return memberRepository.findAll();
}
// 특정 회원 찾기
public Optional<Member> findOne(Long memberId) {
return memberRepository.findById(memberId);
}
}
테스트 코드를 작성하자
class MemberServiceTest {
MemberService memberService;
MemoryMemberRepository memoryMemberRepository;
@BeforeEach
public void beforeEach () {
this.memoryMemberRepository = new MemoryMemberRepository();
this.memberService = new MemberService(memoryMemberRepository);
}
@AfterEach
public void afterEach() {
memoryMemberRepository.clearStore();
}
@Test
void join() {
// given
Member member = new Member();
member.setName("spring");
// when
Long result = memberService.join(member);
// then
Member findMember = memberService.findOne(result).get();
Assertions.assertThat(findMember).isEqualTo(member);
}
@Test
void ExceptionOfJoin(){
// given
Member member1 = new Member();
member1.setName("spring");
memberService.join(member1);
Member member2 = new Member();
member2.setName("spring");
// when, then
Assertions.assertThatThrownBy(() -> memberService.join(member2))
.isInstanceOf(IllegalStateException.class)
.hasMessageContaining("Already Existence");
}
@Test
void findMembers() {
// given
Member member1 = new Member();
member1.setName("spring1");
memberService.join(member1);
Member member2 = new Member();
member2.setName("spring2");
memberService.join(member2);
Member member3 = new Member();
member3.setName("spring3");
memberService.join(member3);
// when
List<Member> result = memberService.findMembers();
// then
Assertions.assertThat(result).contains(member1, member2, member3);
}
@Test
void findOne() {
// given
Member member = new Member();
member.setName("spring");
Long memberId = memberService.join(member);
// when
Member result = memberService.findOne(memberId).get();
// then
Assertions.assertThat(result).isEqualTo(member);
}
}
내부 기능을 구현하였다
이제 컨트롤러를 마무리하여 어플리케이션을 실행시켜보자
@Controller
@RequestMapping("members/")
public class MemberController {
private final MemberService memberService;
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
@GetMapping("new")
public String createForm() {
return "members/createMemberForm";
}
@PostMapping("new")
public String create(MemberForm form) {
Member member = new Member();
member.setName(form.getName());
memberService.join(member);
return "redirect:/";
}
@GetMapping("")
public String list(Model model) {
List<Member> members = memberService.findMembers();
model.addAttribute("members", members);
return "members/memerList";
}
}
create() 메소드에 MemberForm 클래스를 만들어 회원가입 페이지에서 들어오는 정보를 객체로 받아보자
HTML에서 태그에 name 속성의 값과 똑같이 맞춰서 필드를 만들면 스프링이 알아서 값을 넣어준다

public class MemberForm {
// 아래 회원가입 페이지에서 input 태그의 name 속성의 값이 name 인 것을 볼 수 있다
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

createMemberForm.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<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>
memberList.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<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>
</body>
</html>
필요한 페이지와 기능을 모두 작성하였다
그렇지만 실행이 되지 않는다
이유는 스프링에게 Service와 Repository를 등록하지 않았기 때문이다.

@Configuration
public class SpringConfig {
@Bean
public MemberService memberService() {
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
}
실행하여 결과를 확인해보자
같은 이름으로 가입했을 경우 에러가 나오는건 정상이다
맨 위 3가지 목적을 위해 간단히 작업했기에 예외 상황 대처를 해놓은 것이 없다

스프링 가볍게 느껴보자 (2)
이번 목표는 2가지다. DB 접근 기술 4가지 코드 수정없이 Repository 교체하는 방법 Service는 그대로 두고 DB에 접근하는 방법만 교체하면서 동일한 결과를 확인하게 된다. 데이터베이스(DB)를 연결해
cloakinghost.tistory.com
'Spring' 카테고리의 다른 글
| 스프링 가볍게 느껴보자 (3) (0) | 2024.01.11 |
|---|---|
| 스프링 가볍게 느껴보자 (2) (0) | 2024.01.10 |
| API (1) | 2024.01.07 |
| 정적 컨텐츠, MVC와 템플릿 엔진 (0) | 2024.01.07 |
| 배포 파일로 만들어 실행 (0) | 2024.01.07 |