DevLog ๐Ÿ˜ถ

[Spring] ์ˆœ์ˆ˜ ์ž๋ฐ”๋กœ ํšŒ์›-์ฃผ๋ฌธ ์„œ๋น„์Šค ๊ฐœ๋ฐœํ•˜๊ธฐ ๋ณธ๋ฌธ

Back-end/Spring

[Spring] ์ˆœ์ˆ˜ ์ž๋ฐ”๋กœ ํšŒ์›-์ฃผ๋ฌธ ์„œ๋น„์Šค ๊ฐœ๋ฐœํ•˜๊ธฐ

dolmeng2 2022. 8. 6. 19:27

- ๊น€์˜ํ•œ ๋‹˜์˜ '์Šคํ”„๋ง ํ•ต์‹ฌ์›๋ฆฌ - ๊ธฐ๋ณธํŽธ'์„ ๋ณด๊ณ  ์ •๋ฆฌํ•œ ๊ธ€์ž…๋‹ˆ๋‹ค ๐Ÿ˜Š

 

์Šคํ”„๋ง ํ•ต์‹ฌ ์›๋ฆฌ - ๊ธฐ๋ณธํŽธ - ์ธํ”„๋Ÿฐ | ๊ฐ•์˜

์Šคํ”„๋ง ์ž…๋ฌธ์ž๊ฐ€ ์˜ˆ์ œ๋ฅผ ๋งŒ๋“ค์–ด๊ฐ€๋ฉด์„œ ์Šคํ”„๋ง์˜ ํ•ต์‹ฌ ์›๋ฆฌ๋ฅผ ์ดํ•ดํ•˜๊ณ , ์Šคํ”„๋ง ๊ธฐ๋ณธ๊ธฐ๋ฅผ ํ™•์‹คํžˆ ๋‹ค์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค., - ๊ฐ•์˜ ์†Œ๊ฐœ | ์ธํ”„๋Ÿฐ...

www.inflearn.com


| ๊ธฐ๋ณธ ์„ค๊ณ„

<ํšŒ์› ๋„๋ฉ”์ธ>

- ๊ธฐ๋ณธ์ ์œผ๋กœ ์ธํ„ฐํŽ˜์ด์Šค-ํด๋ž˜์Šค ํ˜•ํƒœ๋กœ ์„ค๊ณ„ํ•˜๊ธฐ.

    - ๋ฌผ๋ก , ์‹ค๋ฌด์—์„œ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ์„ฑ์ด ์—†๋Š” ๊ฒฝ์šฐ์—๋Š” ๋ฐ”๋กœ ํด๋ž˜์Šค๋กœ ๋“ค์–ด๊ฐ€๋„ ์ข‹๋‹ค. ์ง€๊ธˆ์€ ์—ฐ์Šต์ด๋‹ˆ๊นŒ!

- ํšŒ์›์€ id, ์ด๋ฆ„, ๋“ฑ๊ธ‰ ์ •๋ณด๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.

    - ์ด๋•Œ, ๋“ฑ๊ธ‰ ์ •๋ณด๋Š” BASIC / VIP ์œ ์ €๋กœ ๋‚˜๋ˆ„์–ด์ ธ ์žˆ๋‹ค.

- ํšŒ์› ์„œ๋น„์Šค๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค.

    - ํšŒ์›๊ฐ€์ž…

    - ํšŒ์› ์กฐํšŒ

- ํšŒ์› ์ €์žฅ์†Œ๋Š” ๋ฉ”๋ชจ๋ฆฌ ์ €์žฅ์†Œ, DB ์ €์žฅ์†Œ, ์™ธ๋ถ€ ์‹œ์Šคํ…œ ์—ฐ๋™ ์ €์žฅ์†Œ๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์„ค๊ณ„ํ•œ๋‹ค.


 

- Spring ๋‚ด๋ถ€์˜ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์—, ๋ณ„๋‹ค๋ฅธ dependency ์ถ”๊ฐ€๋Š” ์ง„ํ–‰ํ•˜์ง€ ์•Š์•˜๋‹ค.

 

| Member

โœ” Domain

[Member.java]

public class Member {
    private Long id;
    private String name;
    private Grade grade;

    public Member(Long id, String name, Grade grade) {
        this.id = id;
        this.name = name;
        this.grade = grade;
    }
}

- ์ฝ”๋“œ ๊ธธ์ด์ƒ getter / setter๋Š” ์ƒ๋žตํ•˜์˜€๋‹ค.

 

[Grade.java]

public enum Grade {
    BASIC, VIP
}

 

โœ” Repository

[MemberRepository.java]

public interface MemberRepository {
    void save(Member member);
    Member findById(Long memberId);
}

[MemoryMemberRepository.java]

public class MemoryMemberRepository implements MemberRepository{
    private static Map<Long, Member> store = new ConcurrentHashMap<>();

    @Override
    public void save(Member member) {
        store.put(member.getId(), member);
    }

    @Override
    public Member findById(Long memberId) {
        return store.get(memberId);
    }
}

- ๋™์‹œ์„ฑ ์ด์Šˆ ๋•Œ๋ฌธ์— ConcurrentHashMap์„ ์‚ฌ์šฉํ•˜์˜€๋‹ค. 

 

 

โœ” Service

[MemberService.java]

public interface MemberService {
    void join(Member member);
    Member findMember(Long memberId);
}

[MemberServiceImpl.java]

public class MemberServiceImpl implements MemberService{
    private final MemberRepository memberRepository = new MemoryMemberRepository();
    @Override
    public void join(Member member) {
        memberRepository.save(member);
    }

    @Override
    public Member findMember(Long memberId) {
        return memberRepository.findById(memberId);
    }
}

 

 

โœ” Test

[MemberServiceTest.java]

class MemberServiceTest {
    MemberService memberService = new MemberServiceImpl();

    @Test
    public void join() throws Exception {
        // given
        Member member = new Member(1L, "hello", Grade.BASIC);

        // when
        memberService.join(member);

        // then
        Member findMember = memberService.findMember(1L);
        Assertions.assertThat(findMember).isEqualTo(member);
    }
}

- ํ…Œ์ŠคํŠธ๋Š” ์ž˜ ํ†ต๊ณผํ•œ๋‹ค. 

- ๊ทธ๋Ÿฌ๋‚˜, ๊ธฐ๋ณธ์ ์œผ๋กœ MemberService๋ฅผ ์„ ์–ธํ•  ๋•Œ MemberServiceImpl() ์ด๋ผ๋Š” ๊ตฌ์ฒด ํด๋ž˜์Šค์— ์˜์กดํ•˜๊ณ  ์žˆ๋‹ค.

- ์ด๋ ‡๊ฒŒ ๋˜๋ฉด ๊ตฌ์ฒด์ ์ธ ๊ฒƒ์— ์˜์กดํ•˜๊ธฐ ๋•Œ๋ฌธ์— DIP ์œ„๋ฐ˜, ์ดํ›„ ์„œ๋น„์Šค ์ •์ฑ…์„ ๋ณ€๊ฒฝํ•  ๋•Œ ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— OCP๊นŒ์ง€ ์œ„๋ฐ˜ํ•˜๊ฒŒ ๋œ๋‹ค.

 

- ์šฐ์„ , ์œ„ ๋ฌธ์ œ๋ฅผ ๊ธฐ์–ตํ•ด๋‘๊ณ  ์ฃผ๋ฌธ์— ๋Œ€ํ•œ ๋„๋ฉ”์ธ์„ ์„ค๊ณ„ํ•ด๋ณด์ž.


- ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์—ญํ• ๊ณผ ๊ตฌํ˜„์„ ๋ถ„๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด, ์ธํ„ฐํŽ˜์ด์Šค-ํด๋ž˜์Šค ํ˜•ํƒœ๋กœ ์„ค๊ณ„ํ•œ๋‹ค.

- ์ฃผ๋ฌธ์€ ํšŒ์› ID, ์ƒํ’ˆ๋ช…, ์ƒํ’ˆ ๊ฐ€๊ฒฉ์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.

    - ์—ฌ๊ธฐ์—, ์ถ”๊ฐ€์ ์œผ๋กœ ํ• ์ธ๋œ ๊ฐ€๊ฒฉ์„ ๋ฆฌํ„ดํ•ด์ฃผ๋Š” ํ•จ์ˆ˜๋ฅผ ์ ์šฉํ•  ์˜ˆ์ •์ด๋‹ค.

- ์ฃผ๋ฌธ ์„œ๋น„์Šค๋Š” ํšŒ์›์„ ์กฐํšŒํ•˜์—ฌ ์‚ฌ์šฉ์ž๊ฐ€ ์ง€๋ถˆํ•  ๊ฐ€๊ฒฉ์„ ๊ณ„์‚ฐํ•œ๋‹ค. VIP๋Š” ํ• ์ธ์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

    - ์ด๋•Œ, ํ• ์ธ ์ •์ฑ…์€ ์ •์•ก ํ• ์ธ ์ •์ฑ… (1000์›), ์ •๋ฅ  ํ• ์ธ ์ •์ฑ…์œผ๋กœ ๋‚˜๋‰œ๋‹ค.


 

| Discount

[DiscountPolicy.java]

public interface DiscountPolicy {
    int discount(Member member, int price);
}

[FixDiscountPolicy.java]

public class FixDiscountPolicy implements DiscountPolicy{
    private int fixDiscountPrice = 1000;

    // 1000์›์„ ๊ณ ์ •์œผ๋กœ ํ• ์ธํ•ด์ฃผ๊ธฐ
    @Override
    public int discount(Member member, int price) {
        if(member.getGrade() == Grade.VIP) {
            return fixDiscountPrice;
        }
        else {
            return 0;
        }
    }
}

- ๋จผ์ € ์ •์•ก ํ• ์ธ ์ •์ฑ…์„ ๊ตฌํ˜„ํ•˜์˜€๋‹ค. ํ• ์ธ ๊ธˆ์•ก์€ 1000์›์ด๋‹ค.

 

 

| Order

โœ” Domain

[Order.java]

public class Order {
    private Long memberId;
    private String itemName;
    private int itemPrice;
    private int discountPrice;

    public int realPrice() {
        return itemPrice - discountPrice;
    }
}

- ์ฝ”๋“œ์—์„œ๋Š” ์ƒ๋žตํ–ˆ์ง€๋งŒ, Getter/Setter, ๊ทธ๋ฆฌ๊ณ  toString์„ ๋„ฃ์–ด์ฃผ์—ˆ๋‹ค.

- ์‹ค์ œ ์‚ฌ์šฉ์ž๊ฐ€ ์ง€๋ถˆํ•  ๊ฐ€๊ฒฉ์„ ๋ฆฌํ„ดํ•ด์ฃผ๊ธฐ ์œ„ํ•ด์„œ realPrice()๋ผ๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜์˜€๋‹ค.

 

 

โœ” Service

[OrderService.java]

public interface OrderService {
    Order createOrder(Long memberId, String itemName, int itemPrice);
}

[OrderServiceImpl.java]

public class OrderServiceImpl implements OrderService{
    private final MemberRepository memberRepository
            = new MemoryMemberRepository();
    private final DiscountPolicy discountPolicy = new FixDiscountPolicy();

    @Override
    public Order createOrder(Long memberId, String itemName, int itemPrice) {
        Member findMember = memberRepository.findById(memberId);
        int discountPrice = discountPolicy.discount(findMember, itemPrice);
        return new Order(memberId, itemName, itemPrice, discountPrice);
    }
}

๐ŸŽˆ ์—ฌ๊ธฐ์„œ DiscountPolicy๋ฅผ ์ฃผ์ž…ํ•˜๋Š” ๋ถ€๋ถ„์„ ์ž˜ ๋ณด์ž. ๊ตฌ์ฒด ํด๋ž˜์Šค์— ์˜์กดํ•˜๊ณ  ์žˆ๋‹ค. (๋’ค์—์„œ ์„ค๋ช…ํ•  ์˜ˆ์ •)

- id๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ํšŒ์›์„ ์ฐพ์•„์„œ, ํšŒ์›์— ๋”ฐ๋ฅธ ํ• ์ธ ๊ฐ€๊ฒฉ์„ ๋ฆฌํ„ด๋ฐ›๋Š”๋‹ค.

- ํ•ด๋‹น ํ• ์ธ๊ฐ€๊ฒฉ์ด ์ ์šฉ๋œ ์ƒˆ๋กœ์šด ์ฃผ๋ฌธ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

 

 

โœ” Test

class OrderServiceTest {
    MemberService memberService = new MemberServiceImpl();
    OrderService orderService = new OrderServiceImpl();

    @Test
    public void createOrder() throws Exception {
        // given
        Member member = new Member(1L, "hello", Grade.VIP);
        memberService.join(member);

        // when
        Order itemA = orderService.createOrder(1L, "itemA", 10000);

        // then
        // 10000์›์—์„œ 1000์› ํ• ์ธ๋ฐ›์•˜์œผ๋‹ˆ๊นŒ 9000์›!
        Assertions.assertThat(itemA.realPrice()).isEqualTo(9000);
    }
}

- ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ ๋งค์šฐ ์ž˜ ๋Œ์•„๊ฐ„๋‹ค.

- ๊ทธ๋Ÿฌ๋‚˜, ์œ„ ์ฝ”๋“œ์˜ ๊ฒฝ์šฐ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๊ตฌ์ฒด ํด๋ž˜์Šค๋ฅผ ์˜์กดํ•˜๊ณ  ์žˆ๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค.

- ๋˜ํ•œ, ์•„์ง ์ƒˆ๋กœ์šด ํ• ์ธ ์ •์ฑ… - ์ •๋ฅ  ํ• ์ธ ์ •์ฑ…์„ ์ ์šฉํ•˜์ง€ ์•Š์€ ์ƒํƒœ์ด๋‹ค.

    - ์ธํ„ฐํŽ˜์ด์Šค - ํด๋ž˜์Šค ์„ค๊ณ„ ๋•๋ถ„์— ์šฐ๋ฆฌ๋Š” ๊ทธ๋ƒฅ ์ •๋ฅ  ํ• ์ธ ์ •์ฑ…์— ๋Œ€ํ•œ ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•œ ๋’ค, ์ ์šฉํ•ด์ฃผ๋ฉด ๋œ๋‹ค!

 


[RateDiscountPolicy.java]

public class RateDiscountPolicy implements DiscountPolicy{
    private int discountPercent = 10; // 10% ํ• ์ธ

    @Override
    public int discount(Member member, int price) {
        if(member.getGrade() == Grade.VIP) {
            return price * discountPercent / 100;
        }
        else
            return 0;
    }
}

- VIP์˜ ๊ฒฝ์šฐ 10% ํ• ์ธ์„ ์ง„ํ–‰ํ•ด์ค€๋‹ค.

 

 

โœ” Test

[RateDiscountPolicyTest.java]

class RateDiscountPolicyTest {
    RateDiscountPolicy discountPolicy = new RateDiscountPolicy();

    @Test
    public void vip1() throws Exception {
        // given
        Member memberVIP = new Member(1L,"VIP", Grade.VIP);
        Member memberBASIC = new Member(2L, "BASIC", Grade.BASIC);

        // when
        int disVIP = discountPolicy.discount(memberVIP, 10000);
        int disBASIC = discountPolicy.discount(memberBASIC, 10000);

        // then
        Assertions.assertThat(disVIP).isEqualTo(1000);
        Assertions.assertThat(disBASIC).isEqualTo(0);

    }
}

- ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ํ…Œ์ŠคํŠธ๋Š” ์ž˜ ๋Œ์•„๊ฐ„๋‹ค.

 

- ๊ทธ๋Ÿฌ๋‚˜, ์ด์ œ ๋ณธ๊ฒฉ์ ์œผ๋กœ ํ• ์ธ์ •์ฑ…์„ ๋ณ€๊ฒฝํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” OrderServiceImpl์˜ ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝํ•ด์•ผ ํ•œ๋‹ค.

    - ์šฐ๋ฆฌ๊ฐ€ ๊ธฐ์กด์— ์งฐ๋˜ ์ฝ”๋“œ์˜ ๊ฒฝ์šฐ, DiscountPolicy๊ฐ€ ๊ตฌ์ฒด ํด๋ž˜์Šค์ธ FixDiscountPolicy๋ฅผ ์˜์กดํ•˜๊ธฐ ๋•Œ๋ฌธ.

    - ์ด๋Ÿฐ ์‹์œผ๋กœ ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝํ•˜์—ฌ ๊ธฐ๋Šฅ์„ ํ™•์žฅํ•˜๋Š” ๊ฒƒ์€ OCP ์œ„๋ฐ˜์ด๋ฉฐ, ๊ตฌ์ฒด ํด๋ž˜์Šค ์˜์กด์œผ๋กœ ์ธํ•œ DIP ์—ญ์‹œ ์œ„๋ฐ˜์ด๋‹ค.

 

- ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์—, ์˜ค์ง ์ธํ„ฐํŽ˜์ด์Šค์—๋งŒ ์˜์กดํ•˜๋„๋ก ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝํ•ด๋ณด์ž.

    - ๊ทธ๋Ÿฌ๋‚˜, ๋‹จ์ˆœํžˆ = new FixDiscountPolicy(); ๋ฅผ ์—†์• ๋ฒ„๋ฆฐ๋‹ค๋ฉด... NPE๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

    - ๋‹น์—ฐํ•˜๋‹ค. ๊ตฌํ˜„์ฒด๊ฐ€ ์—†๋Š”๋ฐ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์—†์œผ๋‹ˆ๊นŒ.

 

- ์šฐ๋ฆฌ๋Š” ๊ตฌํ˜„ ๊ฐ์ฒด๋ฅผ ๋Œ€์‹  ์ƒ์„ฑํ•˜๊ณ  ์ฃผ์ž…ํ•ด์ฃผ๋Š” ์—ญํ• ์„ ํ•ด์ฃผ๋Š” Configuration ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด๋ณผ ๊ฒƒ์ด๋‹ค.

    - ๊ด€์‹ฌ์‚ฌ์˜ ๋ถ„๋ฆฌ๋ฅผ ์œ„ํ•ด, Configuration ์—ญ์‹œ ๋”ฐ๋กœ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด ์ง„ํ–‰ํ•ด์ฃผ๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

 


| ๊ด€์‹ฌ์‚ฌ์˜ ๋ถ„๋ฆฌ

- ์‹คํ–‰๋˜๋Š” ๊ฐ์ฒด๋“ค์€ ์–ด๋–ค ๊ตฌํ˜„์ฒด๊ฐ€ ์ฃผ์ž…๋˜๋Š”์ง€ ์•Œ ์ˆ˜ ์—†์–ด์•ผ ํ•œ๋‹ค. ๊ทธ์ € ๊ฐ์ž์˜ ์—ญํ• ์— ์ถฉ์‹คํ•ด์•ผ ํ•œ๋‹ค.

 

 

[AppConfig.java]

public class AppConfig {
    public MemberService memberService() {
        return new MemberServiceImpl(new MemoryMemberRepository());
    }

    public OrderService orderService() {
        return new OrderServiceImpl(
                new MemoryMemberRepository(),
                new FixDiscountPolicy()
        );
    }
}

- ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด, MemberServiceImpl์— ๋Œ€ํ•œ ๊ตฌ์ฒด์ ์ธ MemberRepository ๊ตฌํ˜„์ฒด๋ฅผ ์—ฌ๊ธฐ์—์„œ ์ฃผ์ž…ํ•˜๋Š” ๊ฑธ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

    - ์‹ค์ œ ๋™์ž‘์— ํ•„์š”ํ•œ ๊ตฌํ˜„ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด์ค€๋‹ค. ๊ตฌํ˜„ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์—ฐ๊ฒฐํ•˜๋Š” ๊ฑด ๋ฌด์กฐ๊ฑด AppConfig์—์„œ ์ง„ํ–‰ํ•œ๋‹ค!

    - ์ด๋Ÿฐ ์‹์œผ๋กœ ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด ๋„ฃ์–ด์ฃผ๋Š” ๊ฑธ '์ƒ์„ฑ์ž ์ฃผ์ž…' ์ด๋ผ๊ณ  ํ•œ๋‹ค.

    - ์ด์ œ, AppConfig์— ์„ ์–ธ๋œ memberService()๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

- orderService() ์—ญ์‹œ ์„ธ๋ถ€์ ์ธ MemberRepository์™€ DiscountPolicy์— ๋Œ€ํ•œ ๊ตฌํ˜„์ฒด๋ฅผ ์—ฌ๊ธฐ์—์„œ ๋„ฃ์–ด์ฃผ์—ˆ๋‹ค.

 

๐ŸŽˆ ๊ทธ๋Ÿฌ๋‚˜, ํ˜„์žฌ ์ฝ”๋“œ์—์„œ MemoryMemberRepository()๋ฅผ 2๋ฒˆ ์„ ์–ธํ•˜๋Š” ๋“ฑ, ์ค‘๋ณต์ด ์กด์žฌํ•˜๋Š” ๊ฑธ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

     - ์ด ๋ถ€๋ถ„์— ๋Œ€ํ•ด์„œ๋Š” ๋’ค์—์„œ ํ•ด๊ฒฐํ•ด๋ณด๋„๋ก ํ•˜์ž.


โœ” ๊ธฐ์กด์— ์„ ์–ธํ•˜์˜€๋˜ ์ฝ”๋“œ ์ˆ˜์ •

[MemberServiceImpl.java]

public class MemberServiceImpl implements MemberService{
    private final MemberRepository memberRepository;

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

    @Override
    public void join(Member member) {
        memberRepository.save(member);
    }

    @Override
    public Member findMember(Long memberId) {
        return memberRepository.findById(memberId);
    }
}

- memberRepository = new MemoryMemberRepository()๋ฅผ ์‚ญ์ œํ•˜๊ณ , ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด memberRepository์— ์ฃผ์ž…๋ฐ›๋„๋ก ํ•˜์˜€๋‹ค.

- ์ด๋Ÿฌ๋ฉด, memberRepository๋Š” ๋” ์ด์ƒ ๊ตฌํ˜„์ฒด๋ฅผ ์˜์กดํ•˜์ง€ ์•Š๊ฒŒ ๋œ๋‹ค!

 

[OrderServiceImpl.java]

public class OrderServiceImpl implements OrderService{
    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }

    @Override
    public Order createOrder(Long memberId, String itemName, int itemPrice) {
        Member findMember = memberRepository.findById(memberId);
        int discountPrice = discountPolicy.discount(findMember, itemPrice);
        return new Order(memberId, itemName, itemPrice, discountPrice);
    }
}

- ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๋ณ€๊ฒฝํ•ด์ฃผ์—ˆ๋‹ค. OrderServiceImpl์€ ์–ด๋–ค ํ• ์ธ ์ •์ฑ…์ด ๋“ค์–ด์˜ค๋Š”์ง€, ์–ด๋–ค DB ์ ‘๊ทผ ๊ธฐ์ˆ ์ด ๋“ค์–ด์˜ค๋Š”์ง€ ๋ชจ๋ฅธ๋‹ค!

- ์ด์ œ, ๊ฐ Impl๋“ค์€ ์˜์กด๊ด€๊ณ„์— ๋Œ€ํ•œ ๊ฑด ์™ธ๋ถ€์— ๋„˜๊ธฐ๊ณ , ์˜ค์ง ๋กœ์ง๋งŒ ์‹คํ–‰ํ•˜๋ฉด ๋œ๋‹ค.

๐Ÿšฉ ์ด๋ ‡๊ฒŒ, DIP๋ฅผ ๋งŒ์กฑํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค. Impl๋“ค์€ ์˜ค์ง ์ถ”์ƒํ™”๋œ ์ธํ„ฐํŽ˜์ด์Šค์—๋งŒ ์˜์กดํ•œ๋‹ค.

 


โœ” Test ์ฝ”๋“œ ์ˆ˜์ •

[MemberServiceTest.java]

class MemberServiceTest {

    MemberService memberService;

    @BeforeEach
    public void beforeEach() {
        AppConfig appConfig = new AppConfig();
        memberService = appConfig.memberService();
    }

    @Test
    public void join() throws Exception {
        // given
        Member member = new Member(1L, "hello", Grade.BASIC);

        // when
        memberService.join(member);

        // then
        Member findMember = memberService.findMember(1L);
        Assertions.assertThat(findMember).isEqualTo(member);
    }
}

- @BeforeEach๋ฅผ ํ†ตํ•ด, ๊ฐ ํ…Œ์ŠคํŠธ๋ฅผ ํ˜ธ์ถœํ•˜๊ธฐ ์ „์— AppConfig๋ฅผ ์„ ์–ธํ•˜์—ฌ ๋‚ด๋ถ€์˜ memberService๋ฅผ ๊ฐ€์ ธ์˜ค๋„๋ก ํ•˜์˜€๋‹ค.

 

[OrderServiceTest.java]

class OrderServiceTest {
    MemberService memberService;
    OrderService orderService;

    @BeforeEach
    public void beforeEach() {
        AppConfig appConfig = new AppConfig();
        memberService = appConfig.memberService();
        orderService = appConfig.orderService();
    }

    @Test
    public void createOrder() throws Exception {
        // given
        Member member = new Member(1L, "hello", Grade.VIP);
        memberService.join(member);

        // when
        Order itemA = orderService.createOrder(1L, "itemA", 10000);

        // then
        // 10000์›์—์„œ 1000์› ํ• ์ธ๋ฐ›์•˜์œผ๋‹ˆ๊นŒ 9000์›!
        Assertions.assertThat(itemA.realPrice()).isEqualTo(9000);

    }
}

- ๋‹ค ์ข‹์€๋ฐ, AppConfig๋ฅผ ์กฐ๊ธˆ ๋” ๊น”๋”ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•ด๋ณด์ž.

[AppConfig.java]

public class AppConfig {
    public MemberService memberService() {
        return new MemberServiceImpl(memberRepository());
    }

    public OrderService orderService() {
        return new OrderServiceImpl(
                memberRepository(),
                discountPolicy()
        );
    }

    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }

    public DiscountPolicy discountPolicy() {
        //return new FixDiscountPolicy();
        return new RateDiscountPolicy();
    }

}

- ์žฌ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋„๋ก MemberRepository์™€ DiscountPolicy์— ๋Œ€ํ•œ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“œ๋Š” ๋ถ€๋ถ„์„ ๋”ฐ๋กœ ํ•จ์ˆ˜๋กœ ๋บ๋‹ค.

    - ์ดํ›„ ๋ณ€๊ฒฝ์ด ๋ฐœ์ƒํ•˜๋”๋ผ๋„ memberRepository(), discountPolicy() ๋‚ด๋ถ€๋งŒ ๋ฐ”๊พธ์–ด ์ฃผ๋ฉด ๋œ๋‹ค!

- ์ด๋Š” ๊ณง, Service๋Š” Service๋งŒ, Repository๋Š” Repository๋งŒ ๊ด€๋ฆฌํ•˜๊ฒŒ ๋˜์–ด ์กฐ๊ธˆ ๋” ๋ช…ํ™•ํ•˜๊ฒŒ ์—ญํ• ๊ณผ ๊ตฌํ˜„ ํด๋ž˜์Šค๋ฅผ ํ•œ๋ˆˆ์— ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 


- ์ง€๊ธˆ๊นŒ์ง€ ์šฐ๋ฆฌ๋Š” SOLID๋ฅผ ๋งŒ์กฑํ•˜๊ธฐ ์œ„ํ•ด์„œ ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ์‹œ๋„๋ฅผ ํ–ˆ๋‹ค.

    - SRP : ๊ด€์‹ฌ์‚ฌ๋ฅผ ๋ถ„๋ฆฌํ•ด์„œ ๊ตฌํ˜„ ๊ฐ์ฒด์˜ ์ƒ์„ฑ ๋ฐ ์—ฐ๊ฒฐ์€ AppConfig์œผ๋กœ, ๊ฐ ๊ฐ์ฒด๋Š” ๋กœ์ง์—๋งŒ ์ง‘์ค‘ํ•˜๋„๋ก.

    - DIP : ๊ตฌ์ฒด ํด๋ž˜์Šค์— ์˜์กดํ•˜์ง€ ์•Š๋„๋ก AppConfig๊ฐ€ ๋Œ€์‹  ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ์— ์˜์กด๊ด€๊ณ„๋ฅผ ์ฃผ์ž…ํ•˜์˜€๋‹ค.

    - OCP : Fix<->Rate๋กœ ํ• ์ธ ์ •์ฑ…์„ ๋ณ€๊ฒฝํ•˜๋”๋ผ๋„ ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ์˜ ๋ณ€๊ฒฝ ์—†์ด AppConfig๋งŒ ์ˆ˜์ •ํ•˜์˜€๋‹ค.

 

- ์ด์ œ, ์ˆœ์ˆ˜ ์ž๋ฐ”๋งŒ์„ ์ด์šฉํ•˜์ง€ ์•Š๊ณ  ์Šคํ”„๋ง์„ ํ†ตํ•ด์„œ ๋ณธ๊ฒฉ์ ์œผ๋กœ ๊ฐœ๋ฐœ์„ ์‹œ์ž‘ํ•ด๋ณด์ž!

 

Comments