Spring ์ ๋ฌธ / 3. ํ์ ๊ด๋ฆฌ ์์ - ๋ฐฑ์๋ ๊ฐ๋ฐ
by rlaehddnd04221. ๋น์ฆ๋์ค ์๊ตฌ์ฌํญ ์ ๋ฆฌ
๋ฐ์ดํฐ : ID, name
๊ธฐ๋ฅ : ๋ฑ๋ก ๋ฐ ์กฐํ
์ ์ : DB๋ ์ค์ ๋์ง ์์.
- ์ผ๋ฐ์ ์น ์ ํ๋ฆฌ์ผ์ด์ ๊ตฌ์กฐ
1. ์ปจํธ๋กค๋ฌ : ์น MVC์ ์ปจํธ๋กค๋ฌ ์ญํ
2. ์๋น์ค : ํต์ฌ ๋น์ฆ๋์ค ๋ก์ง ๊ตฌํ
3. ๋ฆฌํฌ์งํ ๋ฆฌ : DB์ ์ ๊ทผ, ๋๋ฉ์ธ ๊ฐ์ฒด๋ฅผ DB์ ์ ์ฅ ๋ฐ ๊ด๋ฆฌ
4. ๋๋ฉ์ธ : ๋น์ฆ๋์ค ๋๋ฉ์ธ ๊ฐ์ฒด
ex) ํ์, ์ฃผ๋ฌธ, ์ฟ ํฐ ๋ฑ๋ฑ ์ฃผ๋ก DB์ ์ ์ฅํ๊ณ ๊ด๋ฆฌ๋จ.
- ํด๋์ค ์์กด๊ด๊ณ
- DB๊ฐ ์ ์ ๋์ง ์์ ์ํฉ์ด๋ผ ์ฐ์ ์ธํฐํ์ด์ค๋ก ๊ตฌํ ํด๋์ค๋ฅผ ๋ณ๊ฒฝํ ์ ์๋๋ก ์ค๊ณ
- ๋ฐ์ดํฐ ์ ์ฅ์(DB)๋ RDB, NoSQL ๋ฑ๋ฑ ๋ค์ํ ์ ์ฅ์๊ฐ ์์ง๋ง ๊ณ ๋ฏผ์ค์ธ ์ํฉ
- ๊ฐ๋ฐ์งํ์ ์ํด์ ์ด๊ธฐ ๊ฐ๋ฐ ๋จ๊ณ์์๋ ๊ตฌํ์ฒด๋ก ๊ฐ๋ฒผ์ด ๋ฉ๋ชจ๋ฆฌ ๊ธฐ๋ฐ์ ๋ฐ์ดํฐ ์ ์ฅ์ ์ฌ์ฉ
2. ํ์ ๋๋ฉ์ธ๊ณผ Repository ์์ฑ
- ํ์ ๊ฐ์ฒด(๋๋ฉ์ธ) ์์ฑ
package kdo6301.spring0.domain;
public class Member {
private Long id;
private String name;
public Long getId() {
return id;
}
public String getName() {
return name;
}
public void setId(Long id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
}
- ํ์ ๋ฆฌํฌ์งํ ๋ฆฌ ์ธํฐํ์ด์ค
์ ์ฅํ๊ธฐ ์ํ save ๋ฉ์๋, ๊ฒ์์ ์ํ findBy ๋ฉ์๋ ์ ์ธ
package kdo6301.spring0.repository;
import kdo6301.spring0.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();
}
โ๏ธ Optional์ด๋?
๊ฐ๋ฐ์ ํ ๋ ๊ฐ์ฅ ๋ง์ด ๋ฐ์ํ๋ ์์ธ ์ค ํ๋์ธ Null Pointer Exception ( NPE ) ๋ฅผ ํผํ๊ธฐ ์ํด ์ฌ์ฉํ๋ ๋ฌธ๋ฒ
Optional <T> ๋ null๊ฐ์ด ์ฌ ์ ์๋ ๊ฐ์ ๊ฐ์ธ๋ Wrapper ํด๋์ค๋ก , null์ด ๋ฐ์ ํ๋๋ผ๋ NPE๊ฐ ๋ฐ์ํ์ง ์๋๋ก ๋์์ค๋ค.
- ํ์ ๋ฆฌํฌ์งํ ๋ฆฌ ๋ฉ๋ชจ๋ฆฌ ๊ตฌํ์ฒด
package kdo6301.spring0.repository;
import kdo6301.spring0.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) {
// store์ ํด๋นํ๋ 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();
}
}
- Map Collection
/ key, value๋ฅผ ์์ผ๋ก ๊ตฌ์ฑ๋๋ ์์๋ฅผ ๋ค๋ฃจ๋ ์ปฌ๋ ์
/ key๋ฅผ ํตํด value๋ฅผ ์ป์ด๋ธ๋ค.
/ ์ฝ์ : get() ๋ฉ์๋
/ ๊ฒ์ : put() ๋ฉ์๋
- ArrayList Collection
/ C++์ vector์ ๊ฐ์ด ๋ฐฐ์ด์ ํฌ๊ธฐ๊ฐ ๊ฐ๋ณ์ ์ผ๋ก ๋ณํจ
- HashMap Collection
/ Map Collection class์ ์ํ๋ ํด๋์ค
/ HashMap์ Map์ ๊ตฌํํ๋ค. key์ value๋ฅผ ๋ฌถ์ด ํ๋์ entry๋ก ์ ์ฅํ๋ค๋ ํน์ง์ ๊ฐ์ง
/ ๋ง์ฐฌ๊ฐ์ง๋ก key์ value์ ์์ผ๋ก ๊ตฌ์ฑ๋๋ ์์๋ฅผ ๋ค๋ฃจ๋ ์ปฌ๋ ์
/ ๋ง์ฐฌ๊ฐ์ง๋ก value๋ฅผ ๊ฒ์ํ๊ธฐ ์ํด์ key๋ฅผ ์ด์ฉ
/ ์ฝ์ : get() ๋ฉ์๋
/ ๊ฒ์ : put() ๋ฉ์๋
/ ํด์ ์๊ณ ๋ฆฌ์ฆ(hash algorithm)์ ์ฌ์ฉํ์ฌ ๋ง์ ์์ ๋ฐ์ดํฐ๋ฅผ ๊ฒ์ํ๋๋ฐ ๊ฒ์ ์๋๊ฐ ๋งค์ฐ ๋น ๋ฅด๋ค.
- ofNullable() method
of()๋ ofNullable() ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ Optional ๊ฐ์ฒด๋ฅผ ์์ฑํ ์ ์๋ค.
of() ๋ฉ์๋๋ null์ด ์๋ ๋ช ์๋ ๊ฐ์ ์ง๋๋ Optional ๊ฐ์ฒด๋ฅผ ๋ฆฌํด
-> of() ๋ฉ์๋๋ฅผ ํตํด ์์ฑ๋ ๊ฐ์ฒด์ null์ด ์ ์ฅ๋๋ฉด NPE ๋ฐ์
ofNullable() ๋ฉ์๋๋ ๋ช ์๋ ๊ฐ์ด null์ด ์๋๋ฉด ๋ช ์๋ ๊ฐ์ ๊ฐ์ง๋ Optional ๊ฐ์ฒด๋ฅผ ๋ฆฌํด,
๋ช ์๋ ๊ฐ์ด null์ด๋ฉด ๋น์ด์๋ Optional ๊ฐ์ฒด๋ฅผ ๋ฆฌํด
- stream / filter
stream์ ์ปฌ๋ ์ ์ ์ ์ฅ๋์ด ์๋ ์์๋ค์ ํ๋์ฉ ์ฐธ์กฐํ์ฌ ๋๋ค์์ผ๋ก ์ฒ๋ฆฌํ ์ ์๋๋ก ํด์ฃผ๋ iterator
๋ฐ์ดํฐ ์ฒ๋ฆฌ๋ฅผ ๊ฐ๊ฒฐํ๊ฒ ์์ฑ ๊ฐ๋ฅํ๊ณ , inner iterator๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ๋ณ๋ ฌ ์ฒ๋ฆฌ๊ฐ ์ฝ๋ค๊ณ ํ๋ค.
3. ํ์ Repository Test Case ์์ฑ
๊ฐ๋ฐํ ๊ธฐ๋ฅ์ ์คํํด์ ํ ์คํธ ํ ๋ ์๋ฐ์ main ๋ฉ์๋๋ฅผ ํตํด์ ์คํํ๊ฑฐ๋, ์ปจํธ๋กค๋ฌ๋ฅผ ํตํด์ ํด๋น ๊ธฐ๋ฅ์ ์คํํ๊ธฐ์๋ ์คํํ๋ ๋ฐ
์ค๋ ๊ฑธ๋ฆฌ๊ธฐ ๋๋ฌธ์ ์ฌ๋ฌ ํ ์คํธ๋ฅผ ํ๋ฒ์ ์คํํ๊ธฐ ์ด๋ ต๋ค๋ ๋จ์ ์ด ์๋ค.
์๋ฐ๋ JUnit์ด๋ผ๋ ํ๋ ์์ํฌ ํ ์คํธ๋ฅผ ์คํํด์ ์ด๋ฌํ ๋จ์ ์ ํด๊ฒฐํ๋ค.
- ํ์ ๋ฆฌํฌ์งํ ๋ฆฌ ๋ฉ๋ชจ๋ฆฌ ๊ตฌํ์ฒด ํ ์คํธ
import kdo6301.spring0.domain.Member;
import kdo6301.spring0.repository.MemoryMemberRepository;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.assertj.core.api.Assertions;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
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()
{
//given
Member member = new Member();
member.setName("spring1");
//when
repository.save(member);
//then
Member result = repository.findById(member.getId()).get();
assertThat(result).isEqualTo(member);
}
@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);
}
}
โ๏ธ @AfterEach
: ํ๋ฒ์ ์ฌ๋ฌ ๋ฉ์๋์ ํ ์คํธ๋ฅผ ์คํด์๋ฉด ๋ฉ๋ชจ๋ฆฌ DB์ ์ง์ ํ ์คํธ์ ๊ฒฐ๊ณผ๊ฐ ๋จ์ ์ ์๋ค.
์ด๋ ๊ฒ ๊ฒฐ๊ณผ๊ฐ ๋จ๊ฒ ๋๋ฉด ์ด์ ํ ์คํธ๊ฐ ๋ค์ ํ ์คํธ์ ์ํฅ์ ์ฃผ์ด์ ์คํจํ ๊ฐ๋ฅ์ฑ์ด ์๋ค.
AfterEach ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ๋ฉด ๊ฐ ํ ์คํธ๊ฐ ์ข ๋ฃ๋ ๋ ๋ง๋ค ์ด ๊ธฐ๋ฅ์ ์คํํ๋๋ฐ, ๊ฐ ํ ์คํธ๊ฐ ๋๋๋ฉด store๋ฅผ ๋น์ฐ๋๋ก ์ค์ ํด ์ฃผ์๋ค.
โ๏ธโ๏ธ ํ ์คํธ๋ ๋ ๋ฆฝ์ ์ผ๋ก ์คํ๋์ด์ผ ํ๋ค. ์์กด๊ด๊ณ๊ฐ ์๋ ํ ์คํธ๋ ์ข์ ํ ์คํธ๊ฐ ์๋.
4. ํ์ ์๋น์ค ๊ฐ๋ฐ
package kdo6301.spring0.service;
import kdo6301.spring0.domain.Member;
import kdo6301.spring0.repository.MemberRepository;
import kdo6301.spring0.repository.MemoryMemberRepository;
import java.util.List;
import java.util.Optional;
public class MemberService {
/*
private final MemoryMemberRepository memberRepository = new MemoryMemberRepository();
๋ก ์์ฑํ๋ฉด
ํด๋น ์ธํฐํ์ด์ค ๊ตฌํ ๊ฐ์ฒด๋ง ์ฌ์ฉํ ์ ์์ง๋ง,
private final MemberRepository memberRepository = new MemoryMemberRepository();
์ด๋ ๊ฒ ์์ฑํ๋ฉด ์ค๋ฅธ์ชฝ์ MemberRepository ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ ๋ค๋ฅธ ํด๋์ค๋ฅผ ์ฌ์ฉํ ์๋ ์๋ค.
*/
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);
}
}
5. ํ์ ์๋น์ค ํ ์คํธ
๊ธฐ์กด์๋ ํ์ ์๋น์ค ( MemberService ) ๊ฐ MemoryMemberRepository๋ฅผ ์ง์ ์์ฑํ๊ฒ ํด์ฃผ์๋ค.
public class MemberService {
private final MemberRepository memberRepository =
new MemoryMemberRepository();
}
*ํ์ ๋ฆฌํฌ์งํ ๋ฆฌ์ ์ฝ๋๊ฐ
ํ์ ์๋น์ค ์ฝ๋๋ฅผ DI ๊ฐ๋ฅํ๊ฒ ๋ณ๊ฒฝํ๋ค.
public class MemberService {
private final MemberRepository memberRepository;
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
... }
}
package kdo6301.spring0.service;
import kdo6301.spring0.domain.Member;
import kdo6301.spring0.repository.MemberRepository;
import kdo6301.spring0.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 join() {
//given
Member member = new Member();
member.setName("Kim");
//when
Long saveId = memberService.join(member);
//then
Member One = memberService.findOne(saveId).get();
Assertions.assertThat(member.getName()).isEqualTo(One.getName());
}
@Test
void ์ค๋ณตํ์์์ธ()
{
// given
Member mem1 = new Member();
mem1.setName("Spring1");
Member mem2 = new Member();
mem2.setName("Spring1");
// when
memberService.join(mem1);
IllegalStateException e = assertThrows(IllegalStateException.class, () -> memberService.join(mem2));
Assertions.assertThat(e.getMessage()).isEqualTo("์ด๋ฏธ ์กด์ฌํ๋ ํ์์
๋๋ค.");
// try
// {
// memberService.join(mem2);
// fail();
// }catch(IllegalStateException e){
// Assertions.assertThat(e.getMessage()).isEqualTo("์ด๋ฏธ ์กด์ฌํ๋ ํ์์
๋๋ค.");
// }
// then
}
}
โ๏ธ@BeforeEach : ๊ฐ ํ ์คํธ ์คํ ์ ์ ํธ์ถ๋๋ค. ํ ์คํธ๊ฐ ์๋ก ์ํฅ์ด ์๋๋ก ํญ์ ์๋ก์ด ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ , ์์กด๊ด๊ณ๋ ์๋ก ๋งบ์ด์ค๋ค.
-> @AfterEach ์์จ์ฃผ์ด๋ ๋จ.
<์ฐธ๊ณ ์๋ฃ>
https://mangkyu.tistory.com/70
https://devlogofchris.tistory.com/41
https://psychoria.tistory.com/765
'๐ Backend' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
Spring ์ ๋ฌธ / 6. DB ์ ๊ทผ ๊ธฐ์ (0) | 2023.01.19 |
---|---|
Spring ์ ๋ฌธ / 5. ํ์ ๊ด๋ฆฌ ์์ - ์น MVC ๊ฐ๋ฐ (1) | 2023.01.17 |
Spring ์ ๋ฌธ / 4. ์คํ๋ง ๋น๊ณผ ์์กด๊ด๊ณ (0) | 2023.01.17 |
Spring ์ ๋ฌธ / 2. Spring Web ๊ฐ๋ฐ ๊ธฐ์ด (0) | 2023.01.12 |
Spring ์ ๋ฌธ / 1. ํ๋ก์ ํธ ํ๊ฒฝ์ค์ (0) | 2023.01.12 |
๋ธ๋ก๊ทธ์ ์ ๋ณด
Study Repository
rlaehddnd0422