๐Ÿ“š [Spring] #3. ํšŒ์› ๊ด€๋ฆฌ ์˜ˆ์ œ - ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ

์ด๊ฐ€์€ยท2024๋…„ 4์›” 11์ผ
0

Spring

๋ชฉ๋ก ๋ณด๊ธฐ
3/13
post-thumbnail

๐Ÿ“• ๋น„์ฆˆ๋‹ˆ์Šค ์š”๊ตฌ์‚ฌํ•ญ ์ •๋ฆฌ

๋ฐ์ดํ„ฐ : ํšŒ์›ID, ์ด๋ฆ„
๊ธฐ๋Šฅ : ํšŒ์› ๋“ฑ๋ก, ์กฐํšŒ
โœ” ์•„์ง ๋ฐ์ดํ„ฐ ์ €์žฅ์†Œ๊ฐ€ ์„ ์ •๋˜์ง€ ์•Š์Œ(๊ฐ€์ƒ์˜ ์‹œ๋‚˜๋ฆฌ์˜ค) -> ์–ด๋–ค DB๋ฅผ ์‚ฌ์šฉํ• ์ง€ ์ •ํ•˜์ง€ ์•Š์•˜์Œ

๐Ÿ“– ์ผ๋ฐ˜์ ์ธ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ณ„์ธต ๊ตฌ์กฐ

์ปจํŠธ๋กค๋Ÿฌ : ์›น MVC์˜ ์ปจํŠธ๋กค๋Ÿฌ ์—ญํ• 
์„œ๋น„์Šค : ํ•ต์‹ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๊ตฌํ˜„ ex. ํšŒ์›์€ ์ค‘๋ณต ๊ฐ€์ž…์„ ํ•  ์ˆ˜ ์—†๋‹ค
๋ฆฌํฌ์ง€ํ† ๋ฆฌ : ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ ‘๊ทผ, ๋„๋ฉ”์ธ ๊ฐ์ฒด๋ฅผ DB์— ์ €์žฅํ•˜๊ณ  ๊ด€๋ฆฌ
๋„๋ฉ”์ธ : ํšŒ์›, ์ฃผ๋ฌธ, ์ฟ ํฐ ๋“ฑ๋“ฑ ์ฃผ๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•˜๊ณ  ๊ด€๋ฆฌ๋˜๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋„๋ฉ”์ธ ๊ฐ์ฒด

๐Ÿ“– ํด๋ž˜์Šค ์˜์กด๊ด€๊ณ„

  • ์•„์ง ๋ฐ์ดํ„ฐ ์ €์žฅ์†Œ๊ฐ€ ์„ ์ •๋˜์ง€ ์•Š์•„์„œ, ์šฐ์„  ์ธํ„ฐํŽ˜์ด์Šค๋กœ ๊ตฌํ˜„ ํด๋ž˜์Šค๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„
  • ๋ฐ์ดํ„ฐ ์ €์žฅ์†Œ๋Š” RDB, NoSQL ๋“ฑ๋“ฑ ๋‹ค์–‘ํ•œ ์ €์žฅ์†Œ๋ฅผ ๊ณ ๋ฏผ์ค‘์ธ ์ƒํ™ฉ์œผ๋กœ ๊ฐ€์ •
  • ๊ฐœ๋ฐœ์„ ์ง„ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์ดˆ๊ธฐ ๊ฐœ๋ฐœ ๋‹จ๊ณ„์—์„œ๋Š” ๊ตฌํ˜„์ฒด๋กœ ๊ฐ€๋ฒผ์šด ๋ฉ”๋ชจ๋ฆฌ ๊ธฐ๋ฐ˜์˜ ๋ฐ์ดํ„ฐ ์ €์žฅ์†Œ ์‚ฌ์šฉ

๐Ÿ“™ ํšŒ์› ๋„๋ฉ”์ธ๊ณผ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ๋งŒ๋“ค๊ธฐ

๐Ÿ“– ํšŒ์› ๊ฐ์ฒด

public class Member {
    private Long 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;
    }
}
  1. hello.hellospring ์•„๋ž˜ domain ํŒจํ‚ค์ง€๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ํ•ด๋‹น ํŒจํ‚ค์ง€ ์•ˆ์— Member ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค.
  2. ์‹๋ณ„์ž id์™€ ์ด๋ฆ„์„ ๋งŒ๋“ค๊ณ  Getter, Setter๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค.
    โœ” Window ๋‹จ์ถ•ํ‚ค : Alt + Insert

๐Ÿ“– ํšŒ์› ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ์ธํ„ฐํŽ˜์ด์Šค

public interface MemberRepository {
    Member save(Member member); //ํšŒ์›์ด ์ €์žฅ์†Œ์— ์ €์žฅ
    Optional<Member> findById(Long id); //id๋กœ ํšŒ์› ์ฐพ๊ธฐ
    Optional<Member> findByName(String name); //name์œผ๋กœ ํšŒ์› ์ฐพ๊ธฐ
    List<Member> findAll(); //์ง€๊ธˆ๊นŒ์ง€ ์ €์žฅ๋œ ๋ชจ๋“  ํšŒ์› list ๋ฐ˜ํ™˜
}
  1. hello.hellospring ์•„๋ž˜ repository ํŒจํ‚ค์ง€๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ํ•ด๋‹น ํŒจํ‚ค์ง€ ์•ˆ์— MemberRepository Interface๋ฅผ ๋งŒ๋“ค์–ด ์ž‘์„ฑํ–ˆ๋‹ค.

๐ŸŽˆ Optional์ด๋ž€?

โ–ช Optional์€ Java8๋ถ€ํ„ฐ ๋„์ž…๋œ Null์ด ์˜ฌ ์ˆ˜ ์žˆ๋Š” ๊ฐ’์„ ๊ฐ์‹ธ๋Š” Wrapper ํด๋ž˜์Šค๋กœ, ์ฐธ์กฐํ•˜๋”๋ผ๋„ NPE๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋„๋ก ๋„์™€์ค€๋‹ค.
โ–ช NPE๋Š” NullPointerException์œผ๋กœ null ๊ฐ์ฒด ์ฐธ์กฐ์—์„œ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ ค๊ณ  ํ•  ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ์˜ˆ์™ธ์ด๋‹ค.
โ–ช ๋น„์–ด์žˆ๋Š” ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๋Š” ์ƒํ™ฉ์—์„œ Optional ๊ฐ์ฒด๋กœ ๋ฐ˜ํ™˜๊ฐ’์„ ๋ž˜ํ•‘ํ•ด์ค€๋‹ค๋ฉด ๋ฐ˜ํ™˜๋˜๋Š” ๊ฐ’์ด Null์ผ์ง€๋ผ๋„ Optional ๊ฐ์ฒด๋กœ ๋ž˜ํ•‘๋˜์–ด ๋ฐ˜ํ™˜๋˜๊ธฐ ๋•Œ๋ฌธ์— Null๋กœ๋ถ€ํ„ฐ ์•ˆ์ „ํ•ด์ง„๋‹ค. Optional์€ ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ์—๊ฒŒ ๋ช…์‹œ์ ์œผ๋กœ ๋นˆ ๊ฐ’์ผ ์ˆ˜๋„ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๋ ค์ฃผ๊ณ  ๋นˆ ๊ฐ’์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ๋ฅผ ๊ฐ•์ œํ•œ๋‹ค.
โ–ช ์ž๋ฐ”์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ฐ์ฒด๋ฅผ Optional ๊ฐ์ฒด๋กœ ๊ฐ์‹ธ๊ธฐ ์œ„ํ•ด์„œ๋Š” Optional์—์„œ ์ œ๊ณตํ•˜๋Š” of์™€ ofNullable ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. of๋Š” ์ธ์ž๋กœ์„œ Null ๊ฐ’์„ ๋ฐ›์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์ด๊ณ  ofNullable์€ Null ๊ฐ’์„ ํ—ˆ์šฉํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

๐Ÿ“– ํšŒ์› ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ๋ฉ”๋ชจ๋ฆฌ ๊ตฌํ˜„์ฒด

public class MemoryMemberRepository implements MemberRepository {
    private static Map<Long, Member> store = new HashMap<>(); //ํšŒ์›์„ ์ €์žฅ
    private static long sequence = 0L; //0, 1, 2 ๋“ฑ ํ‚ค ๊ฐ’ ์ƒ์„ฑ

    @Override
    public Member save(Member member) {
        member.setId(++sequence); //member id๋Š” ์‹œ์Šคํ…œ์ด ์ƒ์„ฑ, name์€ ํšŒ์›๊ฐ€์ž… ์‹œ ์ž…๋ ฅ ๋ฐ›์œผ๋ฏ€๋กœ ์—ฌ๊ธฐ์„œ ์ง€์ •ํ•˜์ง€ ์•Š์Œ
        store.put(member.getId(), member); //member id์™€ member๋ฅผ ํ•œ ์Œ์œผ๋กœ Map์œผ๋กœ ์„ ์–ธํ•œ store์— ์ €์žฅ
        return member;
    }

    @Override
    public Optional<Member> findById(Long id) {
        return Optional.ofNullable(store.get(id)); //Optional.ofNullable์„ ํ†ตํ•ด ๋ฐ˜ํ™˜ ๊ฐ’์ด null์ด๋”๋ผ๋„ ํด๋ผ์ด์–ธํŠธ์—์„œ ์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋„๋ก ํ•จ
    }

    @Override
    public Optional<Member> findByName(String name) {
        return store.values().stream()
                .filter(member -> member.getName().equals(name)) //ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋„˜์–ด์˜จ name๊ณผ member์˜ name์ด ๊ฐ™์€์ง€ ํ™•์ธ
                .findAny(); //findAny๋Š” ๊ฐ€์žฅ ๋จผ์ € ํƒ์ƒ‰๋˜๋Š” ์š”์†Œ๋ฅผ ๋ฆฌํ„ดํ•˜์—ฌ ํ•˜๋‚˜๋ผ๋„ ๋ฐ˜ํ™˜ํ•จ. ์—†์„ ๊ฒฝ์šฐ null์ด Optional๋กœ ๊ฐ์‹ธ์ ธ ๋ฐ˜ํ™˜
    }

    @Override
    public List<Member> findAll() {
        return new ArrayList<>(store.values()); //store member๋“ค์„ ์ „๋ถ€ ๋ฐ˜ํ™˜
    }
    
    public void clearStore() {
        store.clear();
    }
}
  1. repository ํŒจํ‚ค์ง€ ์•ˆ์— MemoryMemberRepository ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค.
  2. member id๋Š” ์‹œ์Šคํ…œ์ด ์ƒ์„ฑํ•˜๋Š” ๊ฑฐ๊ณ , name์€ ํšŒ์›๊ฐ€์ž… ์‹œ ์ž…๋ ฅ์„ ๋ฐ›์œผ๋ฏ€๋กœ ์—ฌ๊ธฐ์„œ id๋งŒ ์ง€์ •ํ•ด์ค€๋‹ค. member id์™€ member๋ฅผ ํ•œ ์Œ์œผ๋กœ Map์œผ๋กœ ์„ ์–ธํ•œ store์— ์ €์žฅํ•œ๋‹ค.
  3. Optional.ofNullable์„ ํ†ตํ•ด ๋ฐ˜ํ™˜ ๊ฐ’์ด null์ด๋”๋ผ๋„ ํด๋ผ์ด์–ธํŠธ์—์„œ ์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋„๋ก ํ•œ๋‹ค.
  4. ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋„˜์–ด์˜จ name๊ณผ member์˜ name์ด ๊ฐ™์€์ง€ ํ™•์ธํ•˜๊ณ , findAny๋ฅผ ํ†ตํ•ด ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ๋งŒ์•ฝ ์—†์„ ๊ฒฝ์šฐ null์ด Optional๋กœ ๊ฐ์‹ธ์ ธ ๋ฐ˜ํ™˜๋œ๋‹ค.
  5. store member๋“ค์„ ์ „๋ถ€ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

โœ” ํ˜„์žฌ ์˜ˆ์ œ์—์„œ๋Š” ๋™์‹œ์„ฑ ๋ฌธ์ œ๊ฐ€ ๊ณ ๋ ค๋˜์–ด ์žˆ์ง€ ์•Š์ง€๋งŒ ์‹ค๋ฌด์—์„œ๋Š” ConcurrentHashMap, AtomicLong ์‚ฌ์šฉ์„ ๊ณ ๋ คํ•œ๋‹ค.

๐Ÿ“’ ํšŒ์› ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ์ž‘์„ฑ

โœ” ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋Š” ์‰ฝ๊ฒŒ ๋งํ•ด์„œ ์œ„์—์„œ ๋งŒ๋“  ํšŒ์› ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ํด๋ž˜์Šค๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์ž‘๋™ํ•˜๋Š”์ง€๋ฅผ ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.
โ–ช ๊ฐœ๋ฐœํ•œ ๊ธฐ๋Šฅ์„ ์‹คํ–‰ํ•ด์„œ ํ…Œ์ŠคํŠธ ํ•  ๋•Œ ์ž๋ฐ”์˜ main ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด์„œ ์‹คํ–‰ํ•˜๊ฑฐ๋‚˜, ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ํ†ตํ•ด์„œ ํ•ด๋‹น ๊ธฐ๋Šฅ์„ ์‹คํ–‰ํ•œ๋‹ค.
โ–ช ์ด๋Ÿฌํ•œ ๋ฐฉ๋ฒ•์€ ์ค€๋น„ํ•˜๊ณ  ์‹คํ–‰ํ•˜๋Š”๋ฐ ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๊ณ , ๋ฐ˜๋ณต ์‹คํ–‰ํ•˜๊ธฐ ์–ด๋ ต๊ณ  ์—ฌ๋Ÿฌ ํ…Œ์ŠคํŠธ๋ฅผ ํ•œ๋ฒˆ์— ์‹คํ–‰ํ•˜๊ธฐ ์–ด๋ ต๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค.
โ–ช ์ž๋ฐ”๋Š” JUnit์ด๋ผ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ๋กœ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•ด์„œ ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•œ๋‹ค.

๐Ÿ“– ํšŒ์› ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ๋ฉ”๋ชจ๋ฆฌ ๊ตฌํ˜„์ฒด ํ…Œ์ŠคํŠธ

class MemoryMemberRepositoryTest {
    MemoryMemberRepository repository = new MemoryMemberRepository();

    @AfterEach
    public void afterEach() {
        repository.clearStore();
    }

    @Test //import org.junit.jupiter.api.Test
    public void save() {
        Member member = new Member(); //์ƒˆ๋กœ์šด member ์ƒ์„ฑ
        member.setName("spring"); //๋งŒ๋“  member์˜ name์„ spring์œผ๋กœ ์„ค์ •

        repository.save(member); //๋ฆฌํฌ์ง€ํ† ๋ฆฌ์— member ์ €์žฅ

        //๋งŒ๋“  member๊ฐ€ ์ œ๋Œ€๋กœ ์ €์žฅ๋˜์—ˆ๋Š”์ง€ ๊ฒ€์ฆ
        Member result = repository.findById(member.getId()).get(); //๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅ๋œ ๊ฐ’์„ result๋กœ ์ €์žฅ
        assertThat(member).isEqualTo(result); //์ƒ์„ฑํ•œ member์™€ result๊ฐ€ ๊ฐ™์€์ง€ ํ™•์ธ import static org.assertj.core.api.Assertions.*
    }

    @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(); //spring1 ์ด๋ฆ„์„ ๊ฐ€์ง„ member๋ฅผ result์— ์ €์žฅ
        //Member result = repository.findByName("spring2").get(); //์ด๋ ‡๊ฒŒ ์ž‘์„ฑํ•˜๋ฉด ํ…Œ์ŠคํŠธ ์‹คํŒจ
        assertThat(result).isEqualTo(member1); //๊ฒ€์ฆ
    }

    @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();
        assertThat(result.size()).isEqualTo(2); //์ €์žฅ๋œ member์˜ ์ธ์›์ˆ˜๋กœ ์ „์ฒด ๋ฉค๋ฒ„๋ฅผ ์ œ๋Œ€๋กœ ๋ฐ˜ํ™˜ํ–ˆ๋Š”์ง€ ๊ฒ€์ฆ
    }
}
  1. src/test/java/hello.hellospring ์•„๋ž˜ repository ํŒจํ‚ค์ง€๋ฅผ ์ƒ์„ฑํ•˜๊ณ  MemoryMemberRepository ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ ๋‹ค.
  2. ํ…Œ์ŠคํŠธํ•  ๋ฉ”์„œ๋“œ๋“ค์— @Test๋ฅผ ๋ถ™์—ฌ์„œ ์ƒ์„ฑํ•ด์ค€๋‹ค.

๐ŸŽˆ Save

@Test
public void save() {
	Member member = new Member(); //์ƒˆ๋กœ์šด member ์ƒ์„ฑ
	member.setName("spring"); //๋งŒ๋“  member์˜ name์„ spring์œผ๋กœ ์„ค์ •

	repository.save(member); //๋ฆฌํฌ์ง€ํ† ๋ฆฌ์— member ์ €์žฅ

	//๋งŒ๋“  member๊ฐ€ ์ œ๋Œ€๋กœ ์ €์žฅ๋˜์—ˆ๋Š”์ง€ ๊ฒ€์ฆ
	Member result = repository.findById(member.getId()).get(); //๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅ๋œ ๊ฐ’์„ result๋กœ ์ €์žฅ
    assertThat(member).isEqualTo(result); //์ƒ์„ฑํ•œ member์™€ result๊ฐ€ ๊ฐ™์€์ง€ ํ™•์ธ
}
  1. ์ƒˆ๋กœ์šด member๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ƒ์„ฑํ•œ member์˜ name์„ spring์œผ๋กœ ์„ค์ •ํ•œ ํ›„ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์— member๋ฅผ ์ €์žฅํ•œ๋‹ค.
  2. ๋งŒ๋“  member๊ฐ€ ์ œ๋Œ€๋กœ ์ €์žฅ๋˜์—ˆ๋Š”์ง€ ๊ฒ€์ฆํ•˜๊ธฐ ์œ„ํ•ด ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅ๋œ ๊ฐ’์„ result๋กœ ์ €์žฅํ•˜๊ณ , Assertions์˜ assertThat์„ ์‚ฌ์šฉํ•ด์„œ ์ƒ์„ฑํ•œ member์™€ result๊ฐ€ ๊ฐ™์€์ง€ ํ™•์ธํ•œ๋‹ค.
    โœ” Alt + Enter ๋‹จ์ถ•ํ‚ค๋กœ static import๋กœ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค

๐ŸŽˆ findByName

@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(); //spring1 ์ด๋ฆ„์„ ๊ฐ€์ง„ member๋ฅผ result์— ์ €์žฅ
	//Member result = repository.findByName("spring2").get(); //์ด๋ ‡๊ฒŒ ์ž‘์„ฑํ•˜๋ฉด ํ…Œ์ŠคํŠธ ์‹คํŒจ
	assertThat(result).isEqualTo(member1); //๊ฒ€์ฆ
}
  1. ์ƒˆ๋กœ์šด member1์„ ์ƒ์„ฑํ•˜๊ณ  ์ƒ์„ฑํ•œ member์˜ name์„ spring1์œผ๋กœ ์„ค์ •ํ•œ ํ›„ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์— member1์„ ์ €์žฅํ•œ๋‹ค. ์ •๊ตํ•œ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด member2๋„ ๋งŒ๋“ค์–ด์ค€๋‹ค.
    โœ” Alt + F6 ๋‹จ์ถ•ํ‚ค๋กœ rename์„ ํ•  ์ˆ˜ ์žˆ๋‹ค
  2. spring1 ์ด๋ฆ„์„ ๊ฐ€์ง„ member๋ฅผ result์— ์ €์žฅํ•˜๊ณ , assertThat์„ ์‚ฌ์šฉํ•ด์„œ ๊ฒ€์ฆํ•œ๋‹ค.

๐ŸŽˆ findAll

@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();
    assertThat(result.size()).isEqualTo(2); //์ €์žฅ๋œ member์˜ ์ธ์›์ˆ˜๋กœ ์ „์ฒด ๋ฉค๋ฒ„๋ฅผ ์ œ๋Œ€๋กœ ๋ฐ˜ํ™˜ํ–ˆ๋Š”์ง€ ๊ฒ€์ฆ
}
  1. ์ƒˆ๋กœ์šด member1์„ ์ƒ์„ฑํ•˜๊ณ  ์ƒ์„ฑํ•œ member์˜ name์„ spring1์œผ๋กœ ์„ค์ •ํ•œ ํ›„ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์— member1์„ ์ €์žฅํ•˜๊ณ , List ๊ฐœ์ˆ˜๋ฅผ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด member2๋„ ๋งŒ๋“ค์–ด์ค€๋‹ค.
  2. assertThat์—์„œ ์ €์žฅ๋œ member์˜ ์ธ์›์ˆ˜๋กœ ์ „์ฒด ๋ฉค๋ฒ„๋ฅผ ์ œ๋Œ€๋กœ ๋ฐ˜ํ™˜ํ–ˆ๋Š”์ง€ ๊ฒ€์ฆํ•œ๋‹ค.

์ด๋ ‡๊ฒŒ 3๊ฐœ์˜ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ ์„œ class์—์„œ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•˜๊ฒŒ ๋˜๋ฉด findByName()์—์„œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ๊ทธ ์ด์œ ๋Š” findAll()์ด ๋จผ์ € ์‹คํ–‰๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฏธ member1๊ณผ member2์— ๊ฐ’์ด ์ €์žฅ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
โœ” ๋ชจ๋“  ํ…Œ์ŠคํŠธ๋Š” ์ˆœ์„œ์™€ ์ƒ๊ด€์—†์ด ๋ฉ”์„œ๋“œ ๋ณ„๋กœ ๋…๋ฆฝ์ ์œผ๋กœ ์‹คํ–‰๋˜์–ด์•ผ ํ•œ๋‹ค.

๐ŸŽˆ @AfterEach

@AfterEach
public void afterEach() {
	repository.clearStore();
}

ํ•œ๋ฒˆ์— ์—ฌ๋Ÿฌ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๋ฉ”๋ชจ๋ฆฌ DB์— ์ง์ „ ํ…Œ์ŠคํŠธ์˜ ๊ฒฐ๊ณผ๊ฐ€ ๋‚จ์„ ์ˆ˜ ์žˆ๋‹ค. ์ด๋ ‡๊ฒŒ ๋˜๋ฉด ๋‹ค์Œ ์ด์ „ ํ…Œ์ŠคํŠธ ๋•Œ๋ฌธ์— ๋‹ค์Œ ํ…Œ์ŠคํŠธ๊ฐ€ ์‹คํŒจํ•  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋‹ค. @AfterEach๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐ ํ…Œ์ŠคํŠธ๊ฐ€ ์ข…๋ฃŒ๋  ๋•Œ๋งˆ๋‹ค ์ด ๊ธฐ๋Šฅ์„ ์‹คํ–‰ํ•œ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ๋ฉ”๋ชจ๋ฆฌ DB์— ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ญ์ œํ•œ๋‹ค.

๐ŸŽˆ ํ…Œ์ŠคํŠธ ์ฃผ๋„ ๊ฐœ๋ฐœ(TDD)

ํ˜„์žฌ๋Š” MemoryMemberRepository ๊ฐœ๋ฐœ ์™„๋ฃŒ ํ›„ ํ…Œ์ŠคํŠธ ํด๋ž˜์Šค๋ฅผ ์ž‘์„ฑํ–ˆ์ง€๋งŒ ๋ฐ˜๋Œ€๋กœ ํ…Œ์ŠคํŠธ ํด๋ž˜์Šค๋ฅผ ๋จผ์ € ์ž‘์„ฑํ•˜๊ณ  ๊ฐœ๋ฐœ ํด๋ž˜์Šค๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋Š”๋ฐ ์ด๋ฅผ ํ…Œ์ŠคํŠธ ์ฃผ๋„ ๊ฐœ๋ฐœ(TDD)๋ผ๊ณ  ํ•œ๋‹ค.

๐Ÿ“— ํšŒ์› ์„œ๋น„์Šค ๊ฐœ๋ฐœ

์„œ๋น„์Šค ํด๋ž˜์Šค๋Š” ๋น„์ฆˆ๋‹ˆ์Šค์— ๊ฐ€๊นŒ์šด ์ด๋ฆ„์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค. ์„œ๋น„์Šค๋Š” ๋น„์ฆˆ๋‹ˆ์Šค์— ์˜์กด์ ์œผ๋กœ ์„ค๊ณ„ํ•˜๊ณ , ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋Š” ๊ธฐ๊ณ„์ ์œผ๋กœ ๊ฐœ๋ฐœ์ž์Šค๋Ÿฝ๊ฒŒ ์ด๋ฆ„์„ ๋ถ™์ธ๋‹ค.

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()) //findByName ๋ฉ”์„œ๋“œ๋Š” Optional๋กœ ๋ฐ˜ํ™˜๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ”๋กœ ifPresent๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ
                .ifPresent(m -> { //ifPresent๋Š” ๊ฐ’์„ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฉด ์‹คํ–‰ํ•˜๊ณ , ๊ฐ’์ด ์—†์œผ๋ฉด ๋„˜์–ด๊ฐ
                    throw new IllegalStateException("์ด๋ฏธ ์กด์žฌํ•˜๋Š” ํšŒ์›์ž…๋‹ˆ๋‹ค.");
                });
    }

    //์ „์ฒด ํšŒ์› ์กฐํšŒ
    public List<Member> findMembers() {
        return memberRepository.findAll();
    }

    //ํšŒ์› ํ•œ ๋ช… ์กฐํšŒ
    public Optional<Member> findOne(Long memberId) {
        return memberRepository.findById(memberId);
    }
}
  1. src/main/java/hello.hellospring ์•„๋ž˜ service ํŒจํ‚ค์ง€๋ฅผ ์ƒ์„ฑํ•˜๊ณ  MemberService ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ ๋‹ค.
  2. ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์—์„œ ๊ฐ™์€ ์ด๋ฆ„์„ ๊ฐ€์ง„ ์ค‘๋ณต ํšŒ์›์€ ์—†์–ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— validateDuplicateMember๋ฅผ ๋งŒ๋“ค์–ด์„œ ์ค‘๋ณต ํšŒ์›์„ ๊ฒ€์ฆํ•œ๋‹ค.
    โœ” Window ๋ณ€์ˆ˜ ์ถ”์ถœ ๋‹จ์ถ•ํ‚ค : Ctrl + Alt + V
  3. findByName ๋ฉ”์„œ๋“œ๋Š” Optional๋กœ ๋ฐ˜ํ™˜๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ”๋กœ ifPresent๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ifPresent ๋ฉ”์„œ๋“œ๋Š” ๊ฐ’์„ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฉด ์‹คํ–‰ํ•˜๊ณ , ๊ฐ’์ด ์—†์œผ๋ฉด ๋„˜์–ด๊ฐ„๋‹ค.

๐Ÿ“˜ ํšŒ์› ์„œ๋น„์Šค ํ…Œ์ŠคํŠธ

ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ๋ฉ”์„œ๋“œ๋ช…์€ ํ•œ๊ธ€๋กœ ์ž‘์„ฑํ•˜๋Š” ๊ฒฝ์šฐ๋„ ์ข…์ข… ์žˆ๋‹ค. ์ง๊ด€์ ์ด๊ณ , ๋นŒ๋“œ๋  ๋•Œ Test ์ฝ”๋“œ๋Š” ์‹ค์ œ ์ฝ”๋“œ์— ํฌํ•จ๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

ํ…Œ์ŠคํŠธ๋Š” ๋Œ€๋ถ€๋ถ„ ๋ฌด์–ธ๊ฐ€๊ฐ€ ์ฃผ์–ด์กŒ๋Š”๋ฐ(given), ์ด๊ฒƒ์„ ์‹คํ–‰ํ–ˆ์„ ๋•Œ(when), ๊ฒฐ๊ณผ๊ฐ€ ์ด๊ฒŒ ๋‚˜์™€์•ผํ•ด(then) ๋ผ๋Š” ๊ตฌ์กฐ๋กœ ๋‚˜๋‰˜์–ด์ง€๊ธฐ ๋•Œ๋ฌธ์— ์ฃผ์„์œผ๋กœ given, when, then์„ ์ ์–ด๋‘๊ณ  ํ•ด๋‹นํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

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("hello");

        //when
        Long saveId = memberService.join(member);

        //then
        Member findMember = memberService.findOne(saveId).get();
        assertEquals(member.getName(), findMember.getName());
    }

    @Test
    public void ์ค‘๋ณต_ํšŒ์›_์˜ˆ์™ธ() {
        //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("์ด๋ฏธ ์กด์žฌํ•˜๋Š” ํšŒ์›์ž…๋‹ˆ๋‹ค."); //์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋กœ ์ƒํƒœ ๊ฒ€์ฆ
    }
}

โœ” Window ํ…Œ์ŠคํŠธ ํด๋ž˜์Šค ์ž๋™ ์ƒ์„ฑ ๋‹จ์ถ•ํ‚ค : Ctrl + Shift + T
โœ” Window ์ด์ „ ์‹คํ–‰ํ•œ ์ฝ”๋“œ ๋‹ค์‹œ ์‹คํ–‰ ๋‹จ์ถ•ํ‚ค : Shift + F10

public class MemberService {
  private final MemberRepository memberRepository = new MemoryMemberRepository();
}

public class MemberServiceTest {
  MemberService memberService = new MemberService;
  MemberRepository memberRepository = new MemoryMemberRepository();
}

โ–ช ์œ„์˜ ์ฝ”๋“œ์—์„œ ๋ฌธ์ œ๋Š” MemberService์—์„œ ์ƒ์„ฑํ•œ memberRepository์™€ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค์—์„œ ๋งŒ๋“  memberRepository๊ฐ€ ์„œ๋กœ ๋‹ค๋ฅธ ์ธ์Šคํ„ด์Šค๋ผ๋Š” ๊ฒƒ์ด๋‹ค.
โ–ช ์ง€๊ธˆ ์ƒํ™ฉ์—์„  ๋ฌธ์ œ๊ฐ€ ๋˜์ง€ ์•Š๋Š”๋‹ค๊ณ  ํ•ด๋„ memberRepository์˜ store ๊ฐ์ฒด๊ฐ€ static ํ•„๋“œ๊ฐ€ ์•„๋‹ˆ๊ฒŒ ๋˜๋ฉด DB๊ฐ€ ๋‹ค๋ฅธ DB๊ฐ€ ๋˜๋ฉด์„œ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธธ ๊ฒƒ์ด๋‹ค. ๊ทธ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๊ฐ™์€ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋กœ ํ…Œ์ŠคํŠธํ•˜๋Š” ๊ฒƒ์ด ๋งž๋Š”๋ฐ ๋‹ค๋ฅธ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋กœ ํ…Œ์ŠคํŠธ๋˜๊ณ  ์žˆ๋‹ค. ๊ทธ๋ž˜์„œ ๊ฐ™์€ ์ธ์Šคํ„ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ๋ฐ”๊ฟ”์•ผ ํ•œ๋‹ค.
โ–ช MemberRepository๋ฅผ MemberService์—์„œ ์ง์ ‘ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์ƒ์„ฑ์ž ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํ†ตํ•ด ์™ธ๋ถ€๋กœ๋ถ€ํ„ฐ ์ฃผ์ž…๋ฐ›๋„๋ก ๋ฐ”๊ฟ”์ค€๋‹ค. ์ด๋Ÿฐ ๊ฒƒ์„ ์˜์กด์„ฑ ์ฃผ์ž…(DI)์ด๋ผ๊ณ  ํ•œ๋‹ค.

public class MemberService {
  private final MemberRepository memberRepository;

  public MemberService(MemberRepository memberRepository) {
    this.memberRepository = memberRepository;
  }
}

public class MemberServiceTest {
  MemberService memberService;
  MemberRepository memberRepository;
  
  @BeforeEach
  public void beforeEach() {
    memberRepository = new MemoryMemberRepository();
    memberService = new MemberService(memberRepository);
  }
}

@BeforeEach : ๊ฐ ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์ „์— ํ˜ธ์ถœ๋œ๋‹ค. ํ…Œ์ŠคํŠธ๊ฐ€ ์„œ๋กœ ์˜ํ–ฅ์ด ์—†๋„๋ก ํ•ญ์ƒ ์ƒˆ๋กœ์šด ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ์˜์กด๊ด€๊ณ„๋„ ์ƒˆ๋กœ ๋งบ์–ด์ค€๋‹ค.
์˜์กด์„ฑ ์ฃผ์ž…(DI) : ํด๋ž˜์Šค ๊ฐ„ ์˜์กด๊ด€๊ณ„๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ  ์žˆ๋Š” Bean ์ค‘์—์„œ ํ•„์š”ํ•œ ๊ฒƒ์„ ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์ž๋™์œผ๋กœ ์ฃผ์ž…ํ•ด ์ฃผ๋Š” ๊ฒƒ์„ ๋งํ•œ๋‹ค. ์ฆ‰, ์–ด๋–ค ๊ฐ์ฒด๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ์˜์กด ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ๋งŒ๋“ค์–ด์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ, ์ฃผ์ž…๋ฐ›์•„์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋งํ•œ๋‹ค.

๐Ÿ“š ์ฐธ๊ณ ์ž๋ฃŒ

์Šคํ”„๋ง ์ž…๋ฌธ - ์ฝ”๋“œ๋กœ ๋ฐฐ์šฐ๋Š” ์Šคํ”„๋ง ๋ถ€ํŠธ, ์›น MVC, DB ์ ‘๊ทผ ๊ธฐ์ˆ 

profile
๊ฐ€๋ฟก์ด์˜ ๊ณต๋ถ€ ์ƒ์ž๐Ÿ“ฆ

0๊ฐœ์˜ ๋Œ“๊ธ€