이 게시글은 [인프런]스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 강의를 기반으로 작성되었으며 강의를 기억하기 위한 기록물입니다.
출처 : 인프런
Gradle은 의존관계가 있는 라이브러리를 함께 다운로드한다
- Spring Boot 라이브러리
- spring-boot-starter-web
- spring-boot-starter-tomcat: 톰캣 (웹서버)
- spring-webmvc: 스프링 웹 MVC
- spring-boot-starter-thymeleaf: 타임리프 템플릿 엔진(View)
- spring-boot-starter(공통): 스프링 부트 + 스프링 코어 + 로깅
- spring-boot
- spring-core- spring-boot-starter-logging
- logback, slf4j
- 테스트 라이브러리
- junit : 테스트 프레임워크
- mockito : 목 라이브럴
- assertj : 테스트코드를 좀 더 편하게 작성할 수 있도록 도와주는 라이브러리
- spring-test : 스프링 통합 테스트 지원
welcome page(정적 페이지)
src > main > resources > static > index.html
참고 사이트
thymeleaf 템플릿 엔진(동적 페이지)
공식 사이트
// hello > helloSpring > controller > HelloController
@Controller
public class HelloController {
@GetMapping("hello")
public String hello(Model model) {
model.addAttribute("data", "hello!!");
return "hello";
}
}
<!-- src > resources > templates > hello.html -->
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Hello</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<p th:text="'안녕하세요. ' + ${data}" >안녕하세요. 손님</p>
</body>
</html>
URL로 접속하면 서버가 파일을 브라우저로 바로 내려주는 방식
ex) welcome page
서버를 거쳐서 페이지를 변형한 뒤 브라우저로 전송하는 방식(뷰 반환)
controller : 비지니스 로직을 작성하는 등 내부의 작업에 집중해야함
view : 화면을 그리는데에 집중해야함
@Controller
public class HelloController {
@GetMapping("hello-mvc")
public String helloMvc(@RequestParam("name") String name, Model model) {
model.addAttribute("name", name);
return "hello-template";
}
}
<html xmlns:th="http://www.thymeleaf.org">
<body>
<p th:text="'hello ' + ${name}">hello! empty</p>
</body>
</html>
http://localhost:8080/hello-mvc?name=spring
반환값으로 JSON을 사용하여 클라이언트에게 데이터 전달(데이터 반환)
화면은 클라이언트가 알아서 그려야함
@Controller
public class HelloController {
@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;
}
}
}
//회원 도메인 객체
package hello.hellospring.domain;
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;
}
}
//회원 리포지토리 인터페이스
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();
}
//회원 리포지토리 메모리 구현체
package hello.hellospring.repository;
import hello.hellospring.domain.Member;
import java.util.*;
public class MemoryMemberRepository implements MemberRepository {
//메모리 저장용 map
private static Map<Long, Member> store = new HashMap<>();
//ID값 생성을 위한 시퀀스
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 List<Member> findAll() {
return new ArrayList<>(store.values());
}
@Override
public Optional<Member> findByName(String name) {
return store.values().stream()
.filter(member -> member.getName().equals(name))
.findAny();
}
//저장소 초기화
public void clearStore() {
store.clear();
}
}
//회원 리포지토리 테스트케이스
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 java.util.Optional;
import static org.assertj.core.api.Assertions.*;
class MemoryMemberRepositoryTest {
MemoryMemberRepository repository = new MemoryMemberRepository();
@AfterEach
public void afterEach() {
repository.clearStore();
}
@Test
public void save() {
//given
Member member = new Member();
member.setName("spring");
//when
repository.save(member);
//then
Member result = repository.findById(member.getId()).get();
//org.assertj.core.api
assertThat(member).isEqualTo(result);
}
@Test
public void findByName() {
//given
Member member1 = new Member();
member1.setName("spring1");
repository.save(member1);
Member member2 = new Member();
member2.setName("spring2");
repository.save(member2);
//when
Member result = repository.findByName("spring1").get();
//then
assertThat(result).isEqualTo(member1);
}
@Test
public void findAll() {
//given
Member member1 = new Member();
member1.setName("spring1");
repository.save(member1);
Member member2 = new Member();
member2.setName("spring2");
repository.save(member2);
//when
List<Member> result = repository.findAll();
//then
assertThat(result.size()).isEqualTo(2);
}
}
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 {
private final MemberRepository memberRepository = new MemoryMemberRepository();
/**
* 회원가입
*/
public Long join(Member member) {
validateDuplicateMember(member); //중복 회원 검증
memberRepository.save(member);
return member.getId();
}
private void validateDuplicateMember(Member member) {
memberRepository.findByName(member.getName())
.ifPresent(m -> {
throw new IllegalStateException("이미 존재하는 회원입니다.");
});
}
/**
* 전체 회원 조회
*/
public List<Member> findMembers() {
return memberRepository.findAll();
}
public Optional<Member> findOne(Long memberId) {
return memberRepository.findById(memberId);
}
}
package hello.hellospring.service;
import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemoryMemberRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;
class MemberServiceTest {
MemberService memberService;
MemoryMemberRepository memberRepository;
@BeforeEach
public void beforeEach() {
memberRepository = new MemoryMemberRepository();
memberService = new MemberService(memberRepository);
}
@AfterEach
public void afterEach() {
memberRepository.clearStore();
}
@Test
public void 회원가입() throws Exception {
//Given
Member member = new Member();
member.setName("hello");
//When
Long saveId = memberService.join(member);
//Then
Member findMember = memberRepository.findById(saveId).get();
assertEquals(member.getName(), findMember.getName());
}
@Test
public void 중복_회원_예외() throws Exception {
//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("이미 존재하는 회원입니다.");
}
}