λΉμ¦λμ€ μꡬμ¬ν μ 리
μΌλ°μ μΈ μΉ μ΄ν리μΌμ΄μ
κ³μΈ΅ ꡬ쑰
β’
컨νΈλ‘€λ¬ : MVC μ 컨νΈλ‘€λ¬
β’
μλΉμ€ : ν΅μ¬ λΉμ¦λμ€ λ‘μ§ κ΅¬ν
β’
리ν¬μ§ν 리 : λλ©μΈ κ°μ²΄λ₯Ό DB μ μ μ₯/κ΄λ¦¬ , DBμ μ κ·Όνλ μν
β’
λλ©μΈ : DBμ μ μ₯λλ κ°μ²΄ κ΄λ¦¬
νΉμ DBμ μ’
μλμ§ μκ²λ μΈν°νμ΄μ€λ₯Ό μμ±νμ¬μ μΆνμ DBμ λ§λ ν΄λμ€ λ³κ²½ν μ μλλ‘ μ€κ³ν κ²μ.
νμ λλ©μΈκ³Ό 리ν¬μ§ν 리 ꡬν
λλ©μΈ
domain/Member.java
λ©€λ²λ id, name μ΄κ³ , κ°κ°μ λν getter/setter λ₯Ό μ μν΄μ€λ€.
package hello.hellospring.domain;
public class Member {
private Long id; // PK μν
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;
}
}
Java
볡μ¬
리ν¬μ§ν 리
리ν¬μ§ν 리 μΈν°νμ΄μ€
repository/MemberRepository
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();
}
Java
볡μ¬
μ°μ DBμ κ΄λ ¨λ λ©μλμ μΈν°νμ΄μ€λ₯Ό μ μνλ€.
findById μ findByName μμ NULL μ΄ λ¦¬ν΄λμ λμ μμΈλ₯Ό μ½κ² μ²λ¦¬νκΈ° μν΄μ Optional λνΌν΄λμ€λ₯Ό μ¬μ©νλ€.
TIP
option + Enter λ‘ μΈν°νμ΄μ€ λ©μλλ₯Ό μλμΌλ‘ λΆλ¬μ¬ μ μλ€.
리ν¬μ§ν 리 ꡬν체
repository/MemoryMemberRepository
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;
// Long μλ null μ΄ λ€μ΄μ¬ μ μκ³ ,
// long μλ null μ΄ λ€μ΄μ¬ μ μμ΄μ long νμ
μΌλ‘ μ μΈμΈ
@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()); // μ 체 value(=Member)λ€μ 리ν΄
}
}
Java
볡μ¬
νμ 리ν¬μ§ν 리 ν μ€νΈ μ½λ
MemoryMemberRepositoryTest.java
class MemoryMemberRepositoryTest {
MemoryMemberRepository repository = new MemoryMemberRepository();
@Test
public void save(){
Member member = new Member();
member.setName("spring");
repository.save(member);
// λ°©κΈ repositoryμ save ν member μ idλ‘ member get
// λ§μ§λ§μ get() μ Optioanl κ°μ²΄μμ μ€μ κ°μ²΄λ₯Ό μ»κΈ° μν΄ μ¬μ©
Member result = repository.findById(member.getId()).get();
// test
// member μ result κ° κ°μμ§ κ²μ¬
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(){
Member member1 = new Member();
member1.setName("spring1");
repository.save(member1);
Member member2 = new Member();
member2.setName("spring1");
repository.save(member2);
List<Member> result = repository.findAll();
assertThat(result.size()).isEqualTo(2);
}
}
Java
볡μ¬
ν
μ€νΈλ₯Ό λλ €λ³΄κ² λλ©΄ findByName ν
μ€νΈ λ©μλμμ μλ¬κ° λλ€.
ν
μ€νΈ μ½λ λ©μλλ μμλλ‘ μ€νλλλ°, λ°λΌμ μλ‘ μμ‘΄μ μ΄λ©΄ μ λλ€.
μ΄ κ²½μ° findAll() λ©μλμμ repository μ μ΄λ¦μ΄ spring1 μΈ member1 μ save νλλ°,
λ€μ΄μ΄ μ€νλλ findByName() λ©μλμμ μ΄λ¦μ΄ spring1 μΈ κ°μ²΄λ₯Ό μ΄μ©νμ¬ ν
μ€νΈνκ³ μλ€.
μλ‘ λ€λ₯Έ ν
μ€νΈ λ©μλκ° μ¬μ©νλ λ°μ΄ν°μμ μΆ©λμ΄ λ κ²μ΄λ€.
μ΄λ° κ²½μ°λ₯Ό λ°©μ§νκΈ° μν΄μ νλμ ν
μ€νΈλ©μλκ° λλλ©΄ μ¬μ©ν λ°μ΄ν°λ₯Ό μ΄κΈ°ν ν΄μ€μΌ νλ€.
λ ν¬μ§ν 리
repository/MemoryMemberRepository.java
public class MemoryMemberRepository implements MemberRepository{
// μλ΅..
// store μ΄κΈ°ν (ν
μ€νΈ μ½λμμ μ¬μ©)
public void clearStore() {
store.clear();
}
}
Java
볡μ¬
리ν¬μ§ν 리μ store λ₯Ό μ΄κΈ°ννλ ν¨μλ₯Ό λ§λ λ€.
κ·Έλ¦¬κ³ ν
μ€νΈμ½λμμ νλμ ν
μ€νΈλ©μλκ° λλ λλ§λ€ μ΄ ν¨μκ° μ€νλκ²λ νλ€.
λ ν¬μ§ν 리 ν
μ€νΈ
MemoryMemberRepositoryTest.java
class MemoryMemberRepositoryTest {
MemoryMemberRepository repository = new MemoryMemberRepository();
// ν
μ€νΈ λ©μλλ€μ΄ μ°λ λ°μ΄ν°λ€μ λ
립μ μΌλ‘ κ΄λ¦¬νκΈ° μν΄ store μ΄κΈ°ν
@AfterEach
public void afterEach() {
repository.clearStore();
}
@Test
// μλ΅..
}
Java
볡μ¬
@AfterEach μ΄λ
Έν
μ΄μ
μ μ¬μ©νλ©΄ κ° ν
μ€νΈλ©μλκ° λλ λλ§λ€ μμμ μ€νλλ€.
ν
μ€νΈ μ½λ μ€ν μ μ μμ μΌλ‘ ν΅κ³Όλλ€.
TIP
junit μ΄ μλ assertj μμ μ 곡νλ Assertions λ₯Ό μ¬μ©νλ€.
(assertj μμ μ§μνλ λΌμ΄λΈλ¬λ¦¬κ° μ’ λ νΈλ¦¬)
Assertions μμ option+enter λ₯Ό μ
λ ₯νμ¬ static import λ₯Ό μν€λ©΄
import static org.assertj.core.api.Assertions.*; static import λ¬Έμ΄ μΆκ°λμ΄μ μμλ€κ° Assertions λ₯Ό μ μ¨μ£Όκ³ assertThat λ§ μ¬μ©ν μ μκ² λλ€.
cf) ν
μ€νΈμ½λλ₯Ό λ¨Όμ λ§λ€κ³ κ°λ°μ νλ κ²μ TDD(ν
μ€νΈμ£Όλκ°λ°) λΌκ³ νλ€.
νμ μλΉμ€ κ°λ°
νμκ°μ
κ°μ μ΄λ¦μ΄ μλ νμμ΄ μμΌλ©΄ μμΈμ²λ¦¬λ₯Ό νκ²λ ν κ²μ΄λ€.
service/MemberService
public class MemberService {
private final MemberRepository memberRepository = new MemoryMemberRepository();
// νμκ°μ
public Long join(Member member) {
// κ°μ μ΄λ¦μ΄ μλ μ€λ³΅ νμ X
memberRepository.findByName(member.getName())
.ifPresent(m -> {
throw new IllegalStateException("μ΄λ―Έ μ‘΄μ¬νλ νμμ
λλ€.");
});
/* μλ κ°μ λ‘μ§
Optional<Member> result = memberRepository.findByName(member.getName());
// ifPresent : ν΄λΉ λ³μ(result) κ° null μ΄ μλλΌλ©΄(==μ΄λ―Έ μ‘΄μ¬νλ€λ©΄)
result.ifPresent(m -> {
throw new IllegalStateException("μ΄λ―Έ μ‘΄μ¬νλ νμμ
λλ€.");
});
*/
memberRepository.save(member);
return member.getId();
}
}
Java
볡μ¬
Β memberRepository.findByName() μ Optional κ°μ²΄μ΄λ―λ‘ Optional κ°μ²΄μ λ΄μ₯ λ©μλμΈ ifPresent() λ©μλλ₯Ό μ¬μ©νμ¬ null μΈμ§ μλμ§ μ²΄ν¬λ₯Ό ν μ μλ€.
TIP λ©μλ μΆμΆνκΈ°
ctrl + T (리ν©ν λ§ λ¨μΆν€) β 7. λ©μλ μΆμΆ
μ΄λ€ μ½λκΉμ§ λ©μλλ‘ μΆμΆν κ²μΈμ§ μ ν
validateDuplicateMember() λΌλ ν¨μλͺ
μΌλ‘ μΆμΆμ ν λͺ¨μ΅
μ 체 μ½λ
public class MemberService {
private final MemberRepository memberRepository = new MemoryMemberRepository();
// νμκ°μ
public Long join(Member member) {
// κ°μ μ΄λ¦μ΄ μλ μ€λ³΅ νμ κ²μ¦ ν μ€λ³΅ μμΌλ©΄ μμΈμ²λ¦¬
validateDuplicateMember(member);
/* μλ κ°μ λ‘μ§
Optional<Member> result = memberRepository.findByName(member.getName());
// ifPresent : ν΄λΉ λ³μ(result) κ° null μ΄ μλλΌλ©΄(==μ΄λ―Έ μ‘΄μ¬νλ€λ©΄)
result.ifPresent(m -> {
throw new IllegalStateException("μ΄λ―Έ μ‘΄μ¬νλ νμμ
λλ€.");
});
*/
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);
}
}
Java
볡μ¬
νμ μλΉμ€ ν μ€νΈ
MemberServiceTest.java
class MemberServiceTest {
MemberService memberService;
MemoryMemberRepository memberRepository;
// μμ‘΄μ± μ£Όμ
(DI)
@BeforeEach
public void beforeEach() {
memberRepository = new MemoryMemberRepository();
memberService = new MemberService(memberRepository);
}
@AfterEach
public void afterEach() {
memberRepository.clearStore();
}
@Test
void νμκ°μ
() {
// given (μ£Όμ΄μ§ μν©)
Member member = new Member();
member.setName("hello");
// when (κ²μ¦νλ €κ³ νλ μν©)
Long saveId = memberService.join(member);
// then (κ²μ¦)
Member findMember = memberService.findOne(saveId).get();
assertThat(member.getName()).isEqualTo(findMember.getName());
}
@Test
public void μ€λ³΅_νμκ°μ
_μμΈ() {
// given
Member member1 = new Member();
member1.setName("spring");
// member1 κ³Ό μ€λ³΅λ μ΄λ¦μΌλ‘ μ€μ
Member member2 = new Member();
member2.setName("spring");
// when
memberService.join(member1);
// then
IllegalStateException e = assertThrows(IllegalStateException.class, () -> memberService.join(member2));
assertThat(e.getMessage()).isEqualTo("μ΄λ―Έ μ‘΄μ¬νλ νμμ
λλ€.");
/* κ°μ λ‘μ§
try{
memberService.join(member2);
fail();
} catch (IllegalStateException e) {
assertThat(e.getMessage()).isEqualTo("μ΄λ―Έ μ‘΄μ¬νλ νμμ
λλ€.");
}
*/
}
}
Java
볡μ¬
Β DI κ°λ
ν
μ€νΈμ μ νμ±κ³Ό λ
립μ±μ μν΄μ 맀 ν
μ€νΈλ©μλκ° μ€νλ λλ§λ€ μλ‘μ΄ μΈμ€ν΄μ€μ λν΄μ μ€νλμ΄μΌ νλ€.
μ΄λ₯Ό μν΄μ ν
μ€νΈμ½λμμ μλΉμ€μ½λμ μμ‘΄μ±μ μ£Όμ
(DI) ν΄μΌνλ€.
@BeforeEach λ₯Ό ν΅ν΄ λ©μλ μ€ν μ λ§λ€ μλ‘μ΄ μΈμ€ν΄μ€λ₯Ό μμ±νκ³ , κ·Έ μΈμ€ν΄μ€μ λν΄μ ν
μ€νΈμ½λλ₯Ό λλ¦¬κ² λλ€.
Β νμκ°μ
μλΉμ€λ‘μ§ κ°μ κ²½μ°, μ λλ‘ λμνλ μΌμ΄μ€ μ μμΈ μΌμ΄μ€(μ€λ³΅νμ) λ λ€μ λν΄μ ν
μ€νΈ μ½λλ₯Ό μμ±ν΄μΌ νλ€.
μμΈ ν
μ€νΈ μΌμ΄μ€ κ°μ κ²½μ°λ μμΈκ° λ°μνλ μν©μ λν΄μ μ€μ μμΈκ° λΏμ΄μ Έ λμ€λμ§ μ²΄ν¬ν΄λ³΄λ λ°©μμΌλ‘ ν
μ€νΈ μ½λλ₯Ό μμ±νλ€.
Β IllegalStateException e = assertThrows(IllegalStateException.class, () -> memberService.join(member2));
: λ λ²μ§Έ μΈμμΈ λλ€ν¨μ μ€ν μ, 첫 λ²μ§Έ μΈμ(IllegalStateException.class) κ°μΈ StateException μ΄ λ°μνλμ§ νμΈνλ μ½λμ΄λ€.
MemberService.java
public class MemberService {
// ν
μ€νΈμ½λμμ μμ‘΄μ± μ£Όμ
(DI) λ°κΈ° μν¨
private final MemberRepository memberRepository;
// μμ±μ
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
// μλΉμ€λ‘μ§ μλ΅..
}
Java
볡μ¬
Β memberRepository κ° λ³νμ§ μλ κ²μ 보μ₯νκΈ° μν΄μ private final λ‘ μμμ²λ¦¬λ₯Ό νκ³ , μμ±μμμ memberRepository λ©€λ²μ νλΌλ―Έν°λ‘ λ€μ΄μ€λ memberRepository λ³μλ₯Ό ν λΉνλ€.
TIP ν
μ€νΈ μ½λ μλ μμ±
νΉμ λ‘μ§μ ν
μ€νΈμ½λλ₯Ό λ§λ€κ³ μΆμ λ μλμΌλ‘ ν
μ€νΈ μ½λ μμμ λ§λ€ μ μλ€.
ν
μ€νΈ μμ± λ¨μΆμ΄ : cmd + shift + T
μλμ²λΌ λΉ κ»λ°κΈ°λ‘ ν
μ€νΈ μ½λκ° λ§λ€μ΄μ§λ€.