๐ ๋น์ฆ๋์ค ์๊ตฌ์ฌํญ ์ ๋ฆฌ
๊ฐ์ ๋ชฉํ๊ฐ ์ค์ ๋น์ฆ๋์ค๊ฐ ์๋๋ผ ์คํ๋ง์ ์ ๋ฐ์ ์ธ ํ๋ฆ์ ์ดํดํ๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์!
→ ํ์ID ์ ์ด๋ฆ ๋ง ๋ฐ์ดํฐ๋ก ์ฌ์ฉํ๋ค!
→ ๊ธฐ๋ฅ์ ํ์ ๋ฑ๋ก๊ณผ ์กฐํ!!
→ ๊ฐ์์ ์๋๋ฆฌ์ค๋ก ์์ง ๋ฐ์ดํฐ ์ ์ฅ์(DB) ๊ฐ ์ ์ ๋์ง ์์!! (๊ธฐ๋ฅ์ ํ์ ํ๊ณ DB๋ฅผ ์ ์ ํด๋ณด์..)
์ผ๋ฐ์ ์ธ ์น ์ ํ๋ฆฌ์ผ์ด์ ๊ณ์ธต ๊ตฌ์กฐ๋
→ ์ปจํธ๋กค๋ฌ : ์น MVC์ ์ปจํธ๋กค๋ฌ ์ญํ
→ ์๋น์ค : ํต์ฌ ๋น์ฆ๋์ค ๋ก์ง ๊ตฌํ
ex. ์ค๋ณต ๊ฐ์ ์ด ์๋๋ ๋ก์ง ๋ฑ๋ฑ ๋น์ฆ๋์ค ๋๋ฉ์ธ ๊ฐ์ฒด๋ฅผ ๊ฐ์ง๊ณ ํต์ฌ ๋น์ฆ๋์ค ๋ก์ง์ ๊ตฌํํ๋ ๊ณ์ธต์ด๋ค.
→ ๋ฆฌํฌ์งํ ๋ฆฌ : ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ๊ทผ, ๋๋ฉ์ธ ๊ฐ์ฒด๋ฅผ DB์ ์ ์ฅํ๊ณ ๊ด๋ฆฌ
→ ๋๋ฉ์ธ : ๋น์ฆ๋์ค ๋๋ฉ์ธ ๊ฐ์ฒด
ex. ํ์, ์ฃผ๋ฌธ, ์ฟ ํฐ ๋ฑ๋ฑ ์ฃผ๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅํ๊ณ ๊ด๋ฆฌ๋จ
ํด๋์ค ์์กด๊ด๊ณ๋
์์ง ๋ฐ์ดํฐ ์ ์ฅ์๊ฐ ์ ์ ๋์ง ์์๋ค๋ ์๋๋ฆฌ์ค์ด๊ธฐ ๋๋ฌธ์ ์ฐ์ ์ Memory ๊ตฌํ์ฒด๋ฅผ ์ฌ์ฉํ๋ค.
(์ผ๋จ ๊ฐ๋ฐ์ ํด์ผ ํ๋ฏ๋ก ๋จ์ํ๊ฒ Memory ์ ๋ฃ์๋ค ๋บ๋ค ํ๋ ๊ทธ๋ฐ ๊ธฐ๋ณธ์ ์ธ ๊ตฌํ์ฒด๋ฅผ ์ฌ์ฉ → ๋์ค์ RDB, NoSQL ๋ฑ๋ฑ ๋ค์ํ ์ ์ฅ์๋ก ๋ฐ๊ฟ ํ์์ฑ์ด ์์ผ๋ฏ๋ก interface ๋ก ๊ตฌํ ํด๋์ค๋ฅผ ๋ณ๊ฒฝํ ์ ์๋๋ก ์ค๊ณํจ!)
๐ ํ์ ๋๋ฉ์ธ๊ณผ ๋ฆฌํฌ์งํ ๋ฆฌ ๋ง๋ค๊ธฐ
์ฐ์ ํ์ ๋๋ฉ์ธ์ ๋ง๋ค์ด์ฃผ๊ธฐ ์ํด domain package ๋ฅผ ๋ง๋ค์ด์ฃผ๊ณ ๊ทธ ์์ Member.java ๋ฅผ ๋ง๋ค์ด์ค๋ค!
package hello.hellospring.domain;
public class Member {
private Long id; // ๊ณ ๊ฐ์ด ์ ํ๋ id ๊ฐ ์๋๋ผ ๊ตฌ๋ถ์ ์ํด ์์คํ
์ด ์ ํ๋ 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;
}
}
alt + insert ๋ก getter setter ๋ฅผ ๋ง๋ค์ด์ฃผ์๊ณ ~~
์ด๋ ๊ฒ ๋ง๋ค์์ผ๋ฉด!!! ๋ฆฌํฌ์งํ ๋ฆฌ ์ฌ์ฉ์ ์ํด repository package ๋ฅผ ๋ง๋ค์ด์ค ํ์
MemberRepository ๋ผ๋ interface ๋ฅผ ์์ฑ! (interface ์!!!)
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 ์ find~ ๋ฅผ ์ฌ์ฉํด์ ๊ฐ์ ธ์์ ๋ NULL ์ธ ๊ฒฝ์ฐ NULL ์ ๊ทธ๋๋ก ๋ฐํํ๋ ๊ฒ์ด ์๋ Optional ๋ก ๊ฐ์ธ์ ๋ฐํํ๋ ์ญํ ์!
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<>(); // key ๊ฐ ํ์์ id ์ด๋ฏ๋ก Long ์ผ๋ก ํด์ค!
private static long sequence = 0L; // sequence ๋ 0, 1, 2 ์ฒ๋ผ ํค๊ฐ์ ์์ฑํด์ฃผ๋ ์ ์
@Override
public Member save(Member member) {
member.setId(++sequence); //store ์ put ํ๊ธฐ์ ์ sequence ๋ฅผ ํ๋ ์ฌ๋ ค์ค
store.put(member.getId(), member);
return member;
}
@Override
public Optional<Member> findById(Long id) {
return Optional.ofNullable(store.get(id)); // NULL ์ Optional ๋ก ๊ฐ์ธ์ ๋ฐํํด์ค
}
@Override
public Optional<Member> findByName(String name) {
return store.values().stream()
.filter(member -> member.getName().equals(name))
.findAny(); // ๊ฒฐ๊ณผ๊ฐ Optional ๋ก ๋ฐํ๋จ
// loop ๋ฅผ ๋ค ๋๋ฉด์ Map ์์ ํ๋๋ผ๋ ์ฐพ์์ง๋ฉด ๋ฐ๋ก ๋ฐํ -> ๋ง์ฝ ์๋ค๋ฉด Optional ์ NULL์ด ํฌํจ์ด ๋์ ๋ฐํ๋จ
}
@Override
public List<Member> findAll() {
return new ArrayList<>(store.values());
}
}
alt + enter ๋ก implementes methods ๋ฅผ ํด์ค๋ค.
๐ ํ์ ๋ฆฌํฌ์งํ ๋ฆฌ ํ ์คํธ ์ผ์ด์ค ์์ฑ
๊ทธ๋ผ ์ด์ ์์ฑํ ์ฝ๋๊ฐ ์ ๋๋ก ์๋ํ๋์ง ๊ฒ์ฌํด๋ณด๊ฒ ์ต๋๋ค~
test ํด๋ ์๋์ repository package ๋ฅผ ๋ง๋ค์ด ์ค ๋ค์์ MemoryMemberRepositoryTest.java ํ์ผ์ ๋ง๋ค์ด์ค๋๋ค!!
package hello.hellospring.repository;
import hello.hellospring.domain.Member;
import org.assertj.core.api.Assertions;
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 { //๋ค๋ฅธ๋ฐ์ ๊ฐ์ ธ๋ค๊ฐ ์ธ๊ฒ ์๋๋ฏ๋ก public ์ผ๋ก ํ ํ์๋ ์์!
MemoryMemberRepository repository = new MemoryMemberRepository();
@Test // Test ํ ๋ก์ง!!
public void save() {
Member member = new Member();
member.setName("spring");
repository.save(member);
Member result = repository.findById(member.getId()).get();
System.out.println("result = " + (result == member));
assertThat(member).isEqualTo(result);
// System.out ์ ์ฌ์ฉํด์ ์ง์ ์ถ๋ ฅ ๋ด์ฉ์ ํ์ธํ ์ ์์ง๋ง
// assert ๋ฅผ ์ฌ์ฉํ๋ฉด ์ถ๋ ฅํ์ง ์๊ณ ๋ ์ค๋ฅ๋ฅผ ํ์ธํ ์ ์์!!
}
@Test
public void findByName() {
Member member1 = new Member();
member1.setName("spring1");
repository.save(member1);
Member member2 = new Member();
member2.setName("spring2");
repository.save(member2);
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();
member1.setName("spring2");
repository.save(member2);
List<Member> result = repository.findAll();
assertThat(result.size()).isEqualTo(2);
}
}
์ด์ Test ๋ฅผ ์คํํด๋ณด๋ฉด ~~ ์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ ๊ฑธ ํ์ธํ ์ ์๋ค.
์๋๋ฉด Test ๋ ์์ฑํ ์์๋๋ก๊ฐ ์๋๋ผ ์์๋ก ์ ํ๋์ด์ ์งํ๋๊ธฐ ๋๋ฌธ์
์๋ก์ ์์(์์กด๊ด๊ณ )์ ์๊ด์์ด ์ค๊ณ๋์ด์ผ ํจ!
→ ํ๋์ ํ ์คํธ๊ฐ ๋๋ ๋ ๋ง๋ค ๊ณต์ฉ ๋ฐ์ดํฐ(์ ์ฅ์) ๋ฅผ ์ง์์ค์ผ ๋ฌธ์ ๊ฐ ์์!!
๊ทธ๋ฌ๊ธฐ ์ํด์ ~
MemoryMemberRepository ์ ๋ค์์ ๋ด์ฉ์ ์ถ๊ฐํด์ฃผ๊ณ !!
...
public void clearStore() {
store.clear(); // store ์ ์น ๋น์์ค!!
MemoryMemberRepositoryTest.java ํ์ผ์ ๋ค์ ๋ด์ฉ์ ์ถ๊ฐํด์ค๋ค!!
@AfterEach // Test ์ ๋ฉ์๋๊ฐ ๋๋ผ ๋๋ง๋ค ์ด๋ค ๋์์ ํ๋ ์ฝ๋ฐฑ ๋ฉ์๋์ด๋ค!!
public void afterEach() {
repository.clearStore(); // Test ๊ฐ ์ํ๋๊ณ ๋๋ผ ๋ ๋ง๋ค repository ๋ฅผ ์น ์ง์
}
๋์ฑ๊ณต..!!
๐ก TDD (ํ ์คํธ ์ฃผ๋ ๊ฐ๋ฐ) : ํ ์คํธ๋ฅผ ๋จผ์ ๋ง๋ค์ด์ ํ์ ์ก๋ ๊ฐ๋ฐ ๋ฐฉ์!
๐ ํ์ ์๋น์ค ๊ฐ๋ฐ
ํ์๊ฐ์ ์๋น์ค๋ฅผ ๊ฐ๋ฐํด๋ณผ๊ฑด๋ฐ!
๊ฐ์ ์ด๋ฆ์ ํ์์ ๋ฑ๋กํ ์ ์๋ ๋ก์ง์ผ๋ก ์์ฑํด๋ณด์!!!
main ํด๋ ์์ service ๋ผ๋ package๋ฅผ ๋ง๋ค์ด์ฃผ๊ณ !
MemberService.java ํ์ผ์ ์์ฑํ๋ค!
package hello.hellospring.service;
import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;
import java.util.List;
import java.util.Optional;
public class MemberService {
private final MemberRepository memberRepository = new MemoryMemberRepository();
// ํ์๊ฐ์
public Long join(Member member) {
// ๊ฐ์ ์ด๋ฆ์ด ์๋ ์ค๋ณต ํ์ X
validateDuplicateMember(member); //์ค๋ณต ํ์ ๊ฒ์ฆ
memberRepository.save(member);
return member.getId();
}
private void validateDuplicateMember(Member member) {
memberRepository.findByName(member.getName()) // Optional<Member> ๋ก ๋ฐํ๋จ
.ifPresent(m -> { //Optional ๋ก ๋ฐํ๋๋ฏ๋ก ifPresent ๋ฅผ ์ฌ์ฉํ ์ ์๋ค. <-> ๊ธฐ์กด์ด์์ผ๋ฉด if == null ์ ์ฌ์ฉ
throw new IllegalStateException("์ด๋ฏธ ์กด์ฌํ๋ ํ์์
๋๋ค.");
});
}
// ์ ์ฒด ํ์ ์กฐํ
public List<Member> findMembers() {
return memberRepository.findAll();
}
// id๋ก ํ์ ์กฐํ
public Optional<Member> findOne(Long memberId) {
return memberRepository.findById(memberId);
}
}
์ค๋ณต ํ์ ๊ฒ์ฆ ๋ก์ง์
ctrl + alt + m ์ ์ฌ์ฉํ์ฌ ๋ฉ์๋๋ก ๋ง๋ค์ด์ค๋ค!!
์ ์ฒด ํ์ ์กฐํ์ id ํ์ ์กฐํ๋ ์ด๋ฏธ ๋ง๋ค์ด ๋์ ๋ฉ์๋๋ฅผ ์ฌ์ฉ!!
๊ทธ๋ผ ๋.
๐ ํ์ ์๋น์ค ํ ์คํธ
๊ทธ๋ผ ์ด์ ์ ๋์ํ๋์ง ํ ์คํธ๋ฅผ ํด๋ด์ผ๊ฒ ์ฃ ~?
ctrl + shift + t ๋จ์ถํค๋ฅผ ์ฌ์ฉํด์ ๋น ๋ฅด๊ฒ Test ๋ฅผ ์ํ ์ค๋น์์ ์ ํ๊ณ !!
์ด๋ ๊ฒ ๊ธฐ๋ณธ ํ์ด ๋ง๋ค์ด์ง๋๋ค!!
ํ ์คํธ๋ฅผ ์ฒ์ ์ ํ ๋์๋
given(์ํฉ์ด ์ฃผ์ด์ง๊ณ ),
when(์ด๋ฐ ์กฐ๊ฑด์์),
then(์ด๋ฌํ ๊ฒฐ๊ณผ๊ฐ ๋์์ผ ํจ)
์ฃผ์์ ๊ธฐ๋ฐ์ผ๋ก ํ๋ฉด ๊ต์ฅํ ๋์์ด ๋จ!
๐ก test ๋ ์ค์ ์ฝ๋์ ํฌํจ๋์ง ์์ผ๋ฏ๋ก ๊ณผ๊ฐํ๊ฒ ํ๊ธ์ ์ฌ์ฉํ์ฌ ์ง๊ด์ ์ผ๋ก ์คํํ์ฌ๋ ์ข์!!
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 java.util.Optional;
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
void ํ์๊ฐ์
() {
//given (์ํฉ์ด ์ฃผ์ด์ง๋ฉด)
Member member = new Member();
member.setName("spring");
//when (์ด๋ด ๋์)
Long saveId = memberService.join(member);
//then (์ด๊ฒ ๋์์ผ ํจ)
Member findeMember = memberService.findOne(saveId).get();
Assertions.assertThat(member.getName()).isEqualTo(findeMember.getName());
}
@Test
public void ์ค๋ณต_ํ์_์์ธ() {
// member 2 ๋ช
์ ์ด๋ฆ์ด ๋๊ฐ๋ค๋ฉด ์์ธ๊ฐ ๋ฐ์ํด์ผํจ!! -> ์ด๊ฑธ ํ์ธ!
//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));
// assertThrow ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ํน์ ๋ฉ์๋ ํธ์ถ ์ ๋ฐ์ํ ๊ฒ์ผ๋ก ์์๋๋ ์์ธ๋ฅผ ๊ฒ์ฆ
Assertions.assertThat(e.getMessage()).isEqualTo("์ด๋ฏธ ์กด์ฌํ๋ ํ์์
๋๋ค.");
/*
memberService.join(member1);
try {
memberService.join(member2);
fail(); // ์์ธ๊ฐ ๋ฐ์ํ์ง ์์ผ๋ฉด fail ์!!
} catch (IllegalStateException e) {
Assertions.assertThat(e.getMessage()).isEqualTo("์ด๋ฏธ ์กด์ฌํ๋ ํ์์
๋๋ค.");
}
*/
//then
}
}
์ต์ข test ์ฝ๋๋ ์ด๋ ๊ฒ ๋์ค๊ฒ ๋๋ค!
์ฃผ์ ์ฒ๋ฆฌ๋ try catch ๋ฌธ์ผ๋ก ์์ธ๊ฐ ๋ฐ์ํ๋์ง๋ฅผ ํ์ธํ ์๋ ์์ง๋ง
assertThat ์ ์ฌ์ฉํ๋ฉด ํจ์ฌ ๊ฐํธํ๋ค!
๋ ์ฝ๋๋ฅผ ์ดํด๋ณด๋ฉด @BeforeEach ๋ ๋ญ์ง..? ๋ผ๊ณ ํ ์ ์๋๋ฐ
MemberService ์ MemberServiceTest ์ MemoryMemberRepository ๊ฐ ์๋ก ๋ค๋ฅด๋ฏ๋ก
๊ฐ์ ์ธ์คํด์ค๋ฅผ ์ฐ๊ฒ ์ค์ ํด์ฃผ๋๊ฒ ์ข์!
๋ฐ๋ผ์ @AfterEach ์ ๋ฐ๋๋ก Test ๊ฐ ์งํ๋๊ธฐ ์ ์ Repository ๋ฅผ ์ค์ ํด์ฃผ๋ ๋ถ๋ถ์ด๋ผ๊ณ ์๊ฐํ๋ฉด ๋จ!
๊ธฐ์กด์ MemberService.java ์์ new ๋ฅผ ์ด์ฉํ์ฌ memberRepository ๋ฅผ ์ฌ์ฉํ๋ ๋ถ๋ถ๋ ๋ค์๊ณผ ๊ฐ์ด ๋ฐ๊ฟ์ค๋ค!
...
private final MemberRepository memberRepository;
// ์ธ๋ถ์์ Repository๋ฅผ ๋ฃ์ด์ค
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
...
→ ์ด์ Test์ ๊ธฐ์กด ํ์ผ์ด ์๋ก ๊ฐ์ MemoryRepository ๋ฅผ ์ฌ์ฉํ ์ ์๊ฒ ๋จ!