[Spring] 3.ํ์๊ด๋ฆฌ ์์ - ๋ฐฑ์๋ ๊ฐ๋ฐ
1. ๋น์ฆ๋์ค ์๊ตฌ์ฌํญ ์ ๋ฆฌ
- ๋ฐ์ดํฐ : ํ์ID, ์ด๋ฆ
- ๊ธฐ๋ฅ : ํ์ ๋ฑ๋ก, ์กฐํ
- ์์ง ๋ฐ์ดํฐ ์ ์ฅ์๊ฐ ์ ์ ๋์ง ์์ (๊ฐ์์ ์๋๋ฆฌ์ค)
- ์ปจํธ๋กค๋ฌ : ์น MVC์ ์ปจํธ๋กค๋ฌ ์ญํ
- ์๋น์ค : ๋น์ฆ๋์ค ๋๋ฉ์ธ์ ๊ฐ์ง๊ณ ํต์ฌ ๋น์ฆ๋์ค ๋ก์ง ๊ตฌํ (ํ์ ์ค๋ณต๊ฐ์ ์๋๋ ๋ก์ง ๋ฑ)
- ๋ฆฌํฌ์งํ ๋ฆฌ : ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ๊ทผ, ๋๋ฉ์ธ ๊ฐ์ฒด๋ฅผ DB์ ์ ์ฅํ๊ณ ๊ด๋ฆฌ
- ๋๋ฉ์ธ : ๋น์ฆ๋์ค ๋๋ฉ์ธ ๊ฐ์ฒด, ex) ํ์, ์ฃผ๋ฌธ, ์ฟ ํฐ ๋ฑ ์ฃผ๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅํ๊ณ ๊ด๋ฆฌ๋จ
- ์์ง ๋ฐ์ดํฐ ์ ์ฅ์๊ฐ ์ ์ ๋์ง ์์๊ธฐ ๋๋ฌธ์ ์ฐ์ ์ธํฐํ์ด์ค๋ก ๊ตฌํ ํด๋์ค๋ฅผ ๋ณ๊ฒฝํ ์ ์๋๋ก ์ค๊ณ
- ๋ฐ์ดํฐ ์ ์ฅ์๋ RDB, NoSQL๋ฑ ๋ค์ํ ์ ์ฅ์๋ฅผ ๊ณ ๋ฏผ์ค์ธ ์ํฉ์ผ๋ก ๊ฐ์
- ๊ฐ๋ฐ์ ์งํํ๊ธฐ ์ํด ์ด๊ธฐ ๊ฐ๋ฐ ๋จ๊ณ์์๋ ๊ตฌํ์ฒด๋ก ๊ฐ๋ฒผ์ด ๋ฉ๋ชจ๋ฆฌ ๊ธฐ๋ฐ์ ๋ฐ์ดํฐ ์ ์ฅ์ ์ฌ์ฉ
ํด๋์ค ์์กด๊ด๊ณ
1) MemberService : ํ์ ๋น์ฆ๋์ค ๋ก์ง์ ํ์ ์๋น์ค
2) MemberRepository : ํ์ ์ ์ฅํ๋ ๊ฒ์ ์ธํฐํ์ด์ค๋ก ์ค๊ณ
3) MemoryMemberRespository : ๊ตฌํ์ฒด๋ฅผ ๋ฉ๋ชจ๋ฆฌ ๊ตฌํ์ฒด๋ก ๋ง๋ฆ (๋ฉ๋ชจ๋ฆฌ๋ก ๋จ์ํ ์ ์ฅ)
๊ตฌ์ฒด์ ๊ธฐ์ (DB)์ด ์ ์ ์ด ๋๋ฉด ์ธํฐํ์ด์ค๊ฐ ํ์
2. ํ์ ๋๋ฉ์ธ๊ณผ ๋ฆฌํฌ์งํ ๋ฆฌ ๋ง๋ค๊ธฐ
- java - hello.hellosp - domain package ๋ง๋ฆ - Member ํด๋์ค ์์ฑ
package hello.hellosp.domain;
public class Member {
private Long id; // system ๋ฑ๋กํ ๋ 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;
}
}
- java - hello.hellosp - repository package ๋ง๋ฆ - MemberRepository ์ธํฐํ์ด์ค ์์ฑ
package hello.hellosp.repository;
import first.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); // find by null ๋ฐํํ ๋ optional๋ก ๊ฐ์ธ์ ๋ฐํํ๋ ๊ฒ์ด optional์ ๊ธฐ๋ฅ
List<Member> findAll(); // ์ง๊ธ๊น์ง ์ ์ฅ๋ ๋ชจ๋ ํ์ ๋ฆฌ์คํธ ๋ฐํ
}
- Optional : ์์ผ๋ฉด null๋ก ๋ฐํ๋๋๋ฐ ๋ฐํ๋ ๋ optional๋ก ๊ฐ์ธ์ ๋ฐํ๋๋ ๊ฒ
- java - hello.hellosp - repository package - M emoryMemberRepository ํด๋์ค ์์ฑ
public class MemoryMemberRepository implements MemberRepository{
// ์ ์ฅํ ๊ณณ
private static Map<Long, Member> store = new HashMap<>(); // ์ค๋ฌด์์๋ ๋์์ฑ ๋ฌธ์ ๋ก concurrent hash ์จ์ผํจ
private static long sequence = 0L; // ์ํ์ค๋ key ๊ฐ ์์ฑํด์ฃผ๋ ๊ฒ, ์ค๋ฌด์์๋ long๋ณด๋ค๋ atomic long
@Override
public Member save(Member member) {
member.setId(++sequence); // store ๋ฃ๊ธฐ ์ ์ member ์์ด๋ ๊ฐ ์ธํ
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 null
}
@Override
public List<Member> findAll() {
return new ArrayList<>(store.values());
}
}
3. ํ์ ๋ฆฌํฌ์งํฐ๋ฆฌ ํ ์คํธ ์ผ์ด์ค ์์ฑ
- ํ ์คํธ ํ ๋ ์๋ฐ์ main ๋ฉ์๋๋ฅผ ํตํด์ ์คํํ๊ฑฐ๋, ์น ์ดํ๋ฆฌ์ผ์ด์ ์ ์ปจํธ๋กค๋ฌ๋ฅผ ํตํด ํด๋น ๊ธฐ๋ฅ์ ์คํ
-> ์ด๋ฐ ๋ฐฉ๋ฒ์ ๋ฒ๊ฑฐ๋กญ๋ค๋ ๋จ์ ์ด ์๋ค
- ์๋ฐ์ JUnit ํ๋ ์์ํฌ๋ก ํ ์คํธ ์คํ
`src/test/java` ํ์ํด๋์ ํ ์คํธํ ํจํค์ง์ ํด๋์ค ์์ฑ - hello.hellosp - repository package - MemoryMemberRepositorytest class
package hello.hellosp.repository;
import hello.hellosp.domain.Member;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import java.util.List;
class MemoryMemberRepositoryTest {
MemoryMemberRepository repository = new MemoryMemberRepository();
@AfterEach // ๋ฉ์๋ ์คํ ๋๋ ๋๋ง๋ค ๋์ (์ฝ๋ฐฑ ๋ฉ์๋)
public void afterEach() {
repository.clearStore();
}
@Test
public void save() { // test๋ก ๋ฉ์๋ ์คํ๋จ
Member member = new Member(); // ๋ฉ์ธ ๋ฉ์๋ ์ธํ
ํด์ฃผ๋๊ฑฐ๋ ๋น์ท
member.setName("JoonieKim");
repository.save(member); // ์ ์ฅํ ๋ ์์ด๋ ์ธํ
๋จ
Member result = repository.findById(member.getId()).get(); // ์ต์
๋์์ ๊ฐ ๊บผ๋, get์ผ๋ก ๊บผ๋ด๋๊ฒ ์ข์๊ฑด ์๋
// Assertions.assertEquals(member, result); // ์ ์ฅํ ๋ฉค๋ฒ์ ๊ฒฐ๊ณผ๊ฐ์ด ๊ฐ์์ง ๋น๊ต -> ๋
น์๋ถ์ด ๋ธ(์ฑ๊ณต)
// ์์ฆ์ ์๋๊ป ๋ ๋ง์ด ์ (assert J)
Assertions.assertThat(member).isEqualTo(result); // ๋ฉค๋ฒ๊ฐ result๋ ๋๊ฐ์์ง ๋น๊ตํด์ฃผ๋ ๊ฒ
// Assertions์ static import๋ก ํด์ (์ต์
+ ์ํฐ) assertThat ์ฌ์ฉ๊ฐ๋ฅ
}
@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();
Assertions.assertThat(result).isEqualTo(member2);
}
@Test
public void findAll() {
Member member1 = new Member();
member1.setName("spring1");
repository.save(member1);
Member member2 = new Member();
member2.setName("spring2");
repository.save(member2);
List<Member> result = repository.findAll();
Assertions.assertThat(result.size()).isEqualTo(2);
}
}
- ๋ฐ๋ก ๋๋ฆฌ๊ฑฐ๋ ํ ์คํธ ์ ์ฒด ๋๋ฆฌ๋ ๊ฒ ๋ชจ๋ ๊ฐ๋ฅ
[ํ๋ฒ์ ๋๋ ธ์ ๋ ์ค๋ฅ]
WHY?
- ๋ฌด์์๋ก ์คํ๋๊ธฐ ๋๋ฌธ์ ์ด์ ์ ์ ์ฅ๋ ๊ฐ์ฒด (findall ํ ์คํธ ๋ ์ ์ฅ๋ ๊ฐ์ฒด)๊ฐ findbyname์ ๋ถ๋ ค์ด
- ๊ทธ๋ ๋ค๋ฉด ํ๋์ @Test ํ๋๊ฐ ๋๋๋ฉด ํ ์ดํฐ ํด๋ฆฌ์ด ํด์ค์ผํจ
MemoryMemberRepository ๋ง์ง๋ง ๋ถ๋ถ์ ์ถ๊ฐ
public void clearStore() {
store.clear();
}
ํ ์คํธ ์ฃผ๋ ๊ฐ๋ฐ (TDD) : ํ ์คํธ ๋จผ์ ์ค๊ณํ๊ณ ๊ตฌํํ๋ ๋ฐฉ๋ฒ
์ฝ๋๊ฐ ๊ธธ์ด์ง์๋ก ํ ์คํธ ์ฝ๋ ์์ด ๊ฐ๋ฐ ๋ถ๊ฐ๋ฅ!
ํ ์คํธ ๊ณต๋ถ ๊น์ด์๊ฒ ํ๋ ๊ฒ์ด ์ค์!
4. ํ์ ์๋น์ค ๊ฐ๋ฐ
- java - first package - service package ์์ฑ - MemberService ํด๋์ค ์์ฑ
package first.hellospring.service;
import first.hellospring.domain.Member;
import first.hellospring.repository.MemberRepository;
import first.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) {
//๊ฐ์ ์ด๋ฆ์ด ์๋ ์ค๋ณต ํ์ ์๋จ
validateDuplicateMemeber(member); // ์ค๋ณตํ์๊ฒ์ฆ
/* ๋ก์ง์ด ๋์ค๋ ๊ฒฝ์ฐ๋ ๋ฉ์๋๋ก ๋ฝ๋ ๊ฒ์ด ์ข์ */
memberRepository.save(member);
return member.getId();
}
private void validateDuplicateMemeber(Member member) {
memberRepository.findByName(member.getName())
.ifPresent(member1 -> { // ๋ฉค๋ฒ ๊ฐ์ด ์์ผ๋ฉด ๋ก์ง ๋์ (Optional์ด ์๊ธฐ ๋๋ฌธ์ ๊ฐ๋ฅ) ๊ณผ๊ฑฐ : if null
// get ๊ถ์ฅํ์ง ์์, orElseGet๋ ์์ (์์ผ๋ฉด ๊บผ๋ด)
throw new IllegalStateException("์ด๋ฏธ ์กด์ฌํ๋ ํ์์
๋๋ค.");
});
/* ์ข๋ ์ฌ์ด ์ฝ๋
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. ํ์ ์๋น์ค ํ ์คํธ (JUnit)
- ์ฝ๊ฒ ์์ฑํ๋ ๋ฒ
-> MemberService ํด๋์ค์ ctrl + shift + t = test ์์ฑ
- test๋ ๋ด๋ถ์์ ๋ณด๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ ํ๊ธ๋ก๋ ๊ฐ๋ฅ (๋น๋ํ ๋ ํฌํจ๋์ง ์์)
[given - when - then ๋ฌธ๋ฒ]
package first.hellospring.service;
import first.hellospring.domain.Member;
import first.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 static org.junit.jupiter.api.Assertions.*;
class MemberServiceTest {
MemberService memberService;
MemoryMemberRepository memberRepository;
// ํ
์คํธ ์คํํ๊ธฐ ์ ์ ํ
์คํธ ๋ง๋ค ๋ฉ๋ชจ๋ฆฌ๋ฉค๋ฒ repo ๋ง๋ค๊ณ ๊ทธ๊ฑฐ๋ฅผ ๋ฉค๋ฒ repo์ ๋ฃ๊ณ
// ๋ฉค๋ฒ repo๋ฅผ memberservice์ ๋ฃ์ด์ฃผ๋ฉด ๊ฐ์ ๋ฉ๋ชจ๋ฆฌ ๋ฉค๋ฒ repo ์ฌ์ฉ
// = DI (dependency injection) ์์กด์ฑ ์ฃผ์
@BeforeEach
public void beforeEach() {
memberRepository = new MemoryMemberRepository();
memberService = new MemberService(memberRepository);
}
@AfterEach
public void afterEach() {
memberRepository.clearStore();
}
@Test
void signUp() {
// given - when - then ๋ฌธ๋ฒ
// given
Member member = new Member();
member.setName("hello");
// when
Long saveId = memberService.join(member);
// then
Member findMember = memberService.findOne(saveId).get();
Assertions.assertThat(member.getName()).isEqualTo(findMember.getName());
}
/** ์ค๋ณต ํ์ ์์ธ ๊ฒ์ฆ์ด ๋ ์ค์ **/
@Test
public void duplicatedCheck() {
//given
Member member1 = new Member();
member1.setName("spring");
Member member2 = new Member();
member2.setName("spring");
//when
// try {
// memberService.join(member2);
// // ์ด๊ฒ ๋ฉ์ฉกํ ๋์ด๊ฐ๋ฉด fail ์ธ๊ฒ์
// fail();
// } catch (IllegalStateException e) {
// // exception ํฐ์ ธ์ ์ ์์ ์ผ๋ก ์คํํ ๊ฒ
// Assertions.assertThat(e.getMessage()).isEqualTo("์ด๋ฏธ ์กด์ฌํ๋ ํ์์
๋๋ค.");
// }
// ์ข ๋ ์ข์ ๋ฌธ๋ฒ
memberService.join(member1);
assertThrows(IllegalStateException.class, () -> memberService.join(member2));
// ๋ค์ ์๋ member~ ๋ก์ง ์คํํ ๊ฑด๋ฐ ์์ ์์ธ๊ฐ ํฐ์ ธ์ผํ๋ค.
// ๋ฉ์ธ์ง ๊ฒ์ฆ -> cmd option v -> e๊ฐ์ ์ ์ฅํ๊ณ ํ์ธ
// memberService.join(member1);
// IllegalStateException e = assertThrows(IllegalStateException.class, () -> memberService.join(member2));
// Assertions.assertThat(e.getMessage()).isEqualTo("์ด๋ฏธ ์กด์ฌํ๋ ํ์์
๋๋ค.");
}
@Test
void findMembers() {
}
@Test
void findOne() {
}
}
์์กด์ฑ ์ฃผ์
MemberService ํด๋์ค
// private final MemberRepository memberRepository= new MemoryMemberRepository();
// ๋ฉค๋ฒ ์๋น์ค์ new MemoryMemberRepository์ Testcase์ ๊ทธ๊ฒ์ด ๋ค๋ฅธ ์ธ์คํด์ค์. (static์ด๊ธฐ ๋๋ฌธ์ ๋ฌธ์ ๊ฐ ์๋์ง๋ง ์๋๋ผ๋ฉด ๋ฌธ์ ๊ฐ ์๊ธธ ์ ์์)
// ๊ทธ๋ฆฌ๊ณ ๊ฐ์ repository๋ก ํ
์คํธํ๋ ค๊ณ ํ๋๊ฑด๋ฐ ์ง๊ธ ๋ค๋ฅธ ๋ ํ์งํ ๋ฆฌ๋ก ํ
์คํธ ๋๊ณ ์๋ ๊ฒ
// ์์กด์ฑ ์ฃผ์
private final MemberRepository memberRepository;
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
MemberServiceTest ํด๋์ค
// ํ
์คํธ ์คํํ๊ธฐ ์ ์ ํ
์คํธ ๋ง๋ค ๋ฉ๋ชจ๋ฆฌ๋ฉค๋ฒ repo ๋ง๋ค๊ณ ๊ทธ๊ฑฐ๋ฅผ ๋ฉค๋ฒ repo์ ๋ฃ๊ณ
// ๋ฉค๋ฒ repo๋ฅผ memberservice์ ๋ฃ์ด์ฃผ๋ฉด ๊ฐ์ ๋ฉ๋ชจ๋ฆฌ ๋ฉค๋ฒ repo ์ฌ์ฉ
// = DI (dependency injection) ์์กด์ฑ ์ฃผ์
@BeforeEach
public void beforeEach() {
memberRepository = new MemoryMemberRepository();
memberService = new MemberService(memberRepository);
}
* option + enter = ์๋ import
* cmd + shift + enter = ๋ฐ๋ก ๋ด๋ ค๊ฐ๊ธฐ
* shift + fn f6 = rename refactoring
* cmd + option + V
memberRepository.findByName(member.getName()); // ์ด๊ฑฐ ํ๊ณ cmd + option + vํ๋ฉด return ๊ฐ ์ ์ฅ
Optional<Member> result = memberRepository.findByName(member.getName());
* ctrl + T : refactoring ๊ด๋ จ ๋จ์ถํค -> method extract
* ctrl + shift + T : ํด๋์ค์ ๋๊ณ ํ๋ฉด ํด๋น ํด๋์ค์ ๋ํ test ์์ฑ
* ctrl + R : ์ด์ Run ๋ค์ ์คํ
'๐จโ๐ป Web Development > Spring - Intro' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Spring] 6.์คํ๋ง DB ์ ๊ทผ ๊ธฐ์ (0) | 2023.02.14 |
---|---|
[Spring] 5.ํ์ ๊ด๋ฆฌ ์์ - ์น MVC ๊ฐ๋ฐ (0) | 2023.02.13 |
[Spring] 4.์คํ๋ง ๋น๊ณผ ์์กด๊ด๊ณ (0) | 2023.02.11 |
[Spring] 2.์คํ๋ง ์น ๊ฐ๋ฐ ๊ธฐ์ด (1) | 2022.07.01 |
[Spring] 1.ํ๋ก์ ํธ ํ๊ฒฝ์ค์ (0) | 2022.06.30 |
์ต๊ทผ๋๊ธ