> For the complete documentation index, see [llms.txt](https://dodeon.gitbook.io/study/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://dodeon.gitbook.io/study/kimyounghan-spring-core-principle/02-understanding-core-principle/example-member.md).

# 회원 도메인 개발

## 구현

![](/files/-MXa5j8WWDx0nGxvkydu)

{% tabs %}
{% tab title="Grade.java" %}

```java
public enum Grade {
  BASIC,
  VIP
}
```

{% endtab %}

{% tab title="Member.java" %}

```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;
  }

  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;
  }

  public Grade getGrade() {
    return grade;
  }

  public void setGrade(Grade grade) {
    this.grade = grade;
  }
}
```

{% endtab %}

{% tab title="MemberRepository.java" %}

```java
public interface MemberRepository {

  void save(Member member);

  Member findById(Long memberId);
}
```

{% endtab %}

{% tab title="MemoryMemberRepository.java" %}

```java
public class MemoryMemberRepository implements MemberRepository {

  // 여러 곳에서 접근할 수 있는 동시성 이슈 때문에, 실무에서는 ConcurrentHashMap을 쓰는 게 맞다.
  private static Map<Long, Member> store = new HashMap<>();

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

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

{% endtab %}

{% tab title="MemberService.java" %}

```java
public interface MemberService {

  void join(Member member);

  Member findMember(Long memberId);
}
```

{% endtab %}

{% tab title="MemberServiceImpl.java" %}

```java
public class MemberServiceImpl implements MemberService {

  // 인터페이스만 가지고 있으면 구현체가 없어서 NPE가 터진다.
  // 따라서 구현 객체(MemoryMemberRepository)를 선택해줘야 한다.
  private final MemberRepository memberRepository = new MemoryMemberRepository();

  @Override
  public void join(Member member) {

  }

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

{% endtab %}
{% endtabs %}

## 테스트

![](/files/-MXa5j8abRErAeR4-j8m)

이제 위의 다이어그램처럼 실제 객체를 참조하도록 만들어보자.

{% tabs %}
{% tab title="MemberApp.java" %}

```java
public class MemberApp {

  public static void main(String[] args) {
    MemberService memberService = new MemberServiceImpl();
    Member member = new Member(1L, "memberA", Grade.VIP);
    memberService.join(member);

    Member findMember = memberService.findMember(1L);
    System.out.println("new member = " + member.getName());
    System.out.println("findMember = " + findMember.getName());
  }
}
```

{% endtab %}
{% endtabs %}

```
new member = memberA
findMember = memberA
```

스프링 없이 순수 자바 코드로 만들었음에도 값이 제대로 나왔다.

{% tabs %}
{% tab title="MemberServiceTest.java" %}

```java
public class MemberServiceTest {

  MemberService memberService = new MemberServiceImpl();

  @Test
  void join() {
    // given
    Member member = new Member(1L, "memberA", Grade.VIP);

    // when
    memberService.join(member);
    Member findMember = memberService.findMember(1L);

    // then
    Assertions.assertThat(member).isEqualTo(findMember);
  }
```

{% endtab %}
{% endtabs %}

매번 메인 메서드에서 실행해볼 수 없으므로 테스트 코드를 구현해보자.

## 문제점

* 만약 다른 repository로 변경할 OCP 원칙을 준수할까?
* DIP를 잘 지키고 있을까?

```java
public class MemberServiceImpl implements MemberService {

  // 추상화와 구현 모두에 의존하고 있다.
  private final MemberRepository memberRepository = new MemoryMemberRepository();
}
```

의존 관계가 인터페이스 뿐만 아니라 구현까지 모두 의존하는 문제가 있다.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://dodeon.gitbook.io/study/kimyounghan-spring-core-principle/02-understanding-core-principle/example-member.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
